Topic: Instantiation Clarification Sought


Author: fzhong@my-deja.com
Date: 2000/05/02
Raw View
In article <3909C5DC.2C2C692F@attglobal.net>,
  b.thomas@attglobal.net wrote:
> fzhong@my-deja.com wrote:
> >
> > In article <38FDDBA5.BFD22617@attglobal.net>,
> >   b.thomas@attglobal.net wrote:
> >
> > [snip...]
> > > I am not following this case. You prove that the use of
> > >
> > >   template <class T> void f ( const T* const& );
> > >
> > > requires identity conversion. So does the use of
> > >
> > >   template <class T> void f ( const T& );
> > >
> > > Since the first is more specialized than the second, I would think
> > that
> > > the compiler will choose the first function "f(const T* const&)".
But,
> > > g++ chooses the second function "f(constT&)". So, how is g++
correct
> > in
> > > this case?
> >
> > I think g++ is exactly correct.
> > Partial ordering rule is only applied _after_
> > the comparison of ICS(implicit conversion sequence)
> > cannt resolve the call.
>
> Agreed.
>
> > in this case, (int*) to (int const* const&)
> > is better to (int* const&)
> >

Oops, a typo here. it should be (int*) to
(int* const&) is better than to (int const* const&)

>
> So, you are saying that you disagree with the proof by Hyman Rosen
that
> both conversions are identity conversions. Here is what he said:
>
> "Given void f(const int *&) and int *p, the call f(p) results in
> the parameter being directly bound to the argument,
                      ^^^^^^^^^^^^^^^^^
                         illegal, const int* and int* are not
                         reference-compatible.

howerver, Given void f(const int * const&) and int *p, the call f(p)
results in
a temporary const int* pt initialized by p, then the parameter is
bound to the temporary pt.

> according to
> 8.5.3. According to 13.3.3.1.4, this causes the implicit conversion
> sequence for the call to be the identity conversion."
>
> I agree with Hyman on this analysis. Since both are identity
> conversions, none of them is better than the other and so partial
> ordering has to be applied.
>
> Do you find anything wrong in this analysis?
>
> --
> Biju Thomas
>
> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting
with ]
> [ your news-reader.  If that fails, use mailto:std-
c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---
               ]
> [ FAQ: http://reality.sgi.com/austern_mti/std-
c++/faq.html              ]
>
>


Regards

Frank Z.


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: fzhong@my-deja.com
Date: 2000/05/02
Raw View
In article <390C607A.7EEAE2A1@attglobal.net>,
  b.thomas@attglobal.net wrote:
 [snip...]
> > Oops, a typo here. it should be (int*) to
> > (int* const&) is better than to (int const* const&)
> >
>
> I will appreciate if you can tell why it is so. My understanding is
that
> both require identity conversions.
>
>   -- "int*" to "int* const&" is simple identity conversion

    correct.

>   -- "int*" to "const int* const&" is identity conversion

    no. a temporary is required here. e.g.
    int*p to const int* p_temporary, this is a
    qualification conversion.

[snip...]
>
>   template<typename T> void f2(const T* const&);
>   template<typename T> void f2(const T&);
>
 actually, I believe that the first template function
 is not deducible from int*p

 Regards

 Frank Z.


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: fzhong@my-deja.com
Date: 2000/04/28
Raw View
Hi, Mr. Meyers
I tried your code with KAI compiler V.3.3, WinNT.
I get the following result
...
int main()
{
  int *ptrToInt = 0;
  f1(ptrToInt);         // T*:  kai 3.3

  f2(ptrToInt);         // T&:  kai 3.3

  f3(ptrToInt);         // T*:  kai 3.3

...
}

after digging into the standard, I believe
g++ is correct in all three cases.

f1 is same as f3, during overload
resolving the top-level cv qualifier
is ignored.
f2 is obvious,
the first one
template<typename T>
void f2(const T* const&);
is not deductable.

so let's focus on f1.
after deducing, we get
void f<int>(int const*);
void f<int*>(int* const&);

as per the standard 13.3.3.2
partial ordering rule is applicable
only after the comparison of
ICS(implicit conversion sequence)
fails.

while in our case, (int*) to (int* const&)
is better than (int*) to (int const*);


regards
Frank Z.


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: fzhong@my-deja.com
Date: 2000/04/29
Raw View
In article <38FDDBA5.BFD22617@attglobal.net>,
  b.thomas@attglobal.net wrote:

[snip...]
> I am not following this case. You prove that the use of
>
>   template <class T> void f ( const T* const& );
>
> requires identity conversion. So does the use of
>
>   template <class T> void f ( const T& );
>
> Since the first is more specialized than the second, I would think
that
> the compiler will choose the first function "f(const T* const&)". But,
> g++ chooses the second function "f(constT&)". So, how is g++ correct
in
> this case?

I think g++ is exactly correct.
Partial ordering rule is only applied _after_
the comparison of ICS(implicit conversion sequence)
cannt resolve the call.
in this case, (int*) to (int const* const&)
is better to (int* const&)

quote form the standard
13.3.3.2
Given these definitions, a viable function F1 is defined to be a better
function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence
than ICSi(F2), and then
       for some argument j, ICSj(F1) is a better conversion sequence than
ICSj(F2), or, if not that,
       F1 is a non-template function and F2 is a template function
specialization, or, if not that,
       F1 and F2 are template functions, and the function template for F1
is more specialized than the tem-plate
for F2 according to the partial ordering rules described in 14.5.5.2,
or, if not that
...

regards

Frank Z


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Biju Thomas <b.thomas@attglobal.net>
Date: 2000/04/29
Raw View
fzhong@my-deja.com wrote:
>
> In article <38FDDBA5.BFD22617@attglobal.net>,
>   b.thomas@attglobal.net wrote:
>
> [snip...]
> > I am not following this case. You prove that the use of
> >
> >   template <class T> void f ( const T* const& );
> >
> > requires identity conversion. So does the use of
> >
> >   template <class T> void f ( const T& );
> >
> > Since the first is more specialized than the second, I would think
> that
> > the compiler will choose the first function "f(const T* const&)". But,
> > g++ chooses the second function "f(constT&)". So, how is g++ correct
> in
> > this case?
>
> I think g++ is exactly correct.
> Partial ordering rule is only applied _after_
> the comparison of ICS(implicit conversion sequence)
> cannt resolve the call.

Agreed.

> in this case, (int*) to (int const* const&)
> is better to (int* const&)
>

So, you are saying that you disagree with the proof by Hyman Rosen that
both conversions are identity conversions. Here is what he said:

"Given void f(const int *&) and int *p, the call f(p) results in
the parameter being directly bound to the argument, according to
8.5.3. According to 13.3.3.1.4, this causes the implicit conversion
sequence for the call to be the identity conversion."

I agree with Hyman on this analysis. Since both are identity
conversions, none of them is better than the other and so partial
ordering has to be applied.

Do you find anything wrong in this analysis?

--
Biju Thomas

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: fzhong@my-deja.com
Date: 2000/04/29
Raw View
In article <3909C5DC.2C2C692F@attglobal.net>,
  b.thomas@attglobal.net wrote:
> fzhong@my-deja.com wrote:
> >
> > In article <38FDDBA5.BFD22617@attglobal.net>,
> >   b.thomas@attglobal.net wrote:
> >
> > [snip...]
> > > I am not following this case. You prove that the use of
> > >
> > >   template <class T> void f ( const T* const& );
> > >
> > > requires identity conversion. So does the use of
> > >
> > >   template <class T> void f ( const T& );
> > >
> > > Since the first is more specialized than the second, I would think
> > that
> > > the compiler will choose the first function "f(const T* const&)".
But,
> > > g++ chooses the second function "f(constT&)". So, how is g++
correct
> > in
> > > this case?
> >
> > I think g++ is exactly correct.
> > Partial ordering rule is only applied _after_
> > the comparison of ICS(implicit conversion sequence)
> > cannt resolve the call.
>
> Agreed.
>
> > in this case, (int*) to (int const* const&)
> > is better to (int* const&)
> >

Oops, a typo here. it should be (int*) to
(int* const&) is better than to (int const* const&)

>
> So, you are saying that you disagree with the proof by Hyman Rosen
that
> both conversions are identity conversions. Here is what he said:
>
> "Given void f(const int *&) and int *p, the call f(p) results in
> the parameter being directly bound to the argument,
                      ^^^^^^^^^^^^^^^^^
                         illegal, const int* and int* are not
                         reference-compatible.

howerver, Given void f(const int * const&) and int *p, the call f(p)
results in
a temporary const int* pt initialized by p, then the parameter is
bound to the temporary pt.

> according to
> 8.5.3. According to 13.3.3.1.4, this causes the implicit conversion
> sequence for the call to be the identity conversion."
>
> I agree with Hyman on this analysis. Since both are identity
> conversions, none of them is better than the other and so partial
> ordering has to be applied.
>
> Do you find anything wrong in this analysis?
>
> --
> Biju Thomas
>
> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting
with ]
> [ your news-reader.  If that fails, use mailto:std-
c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---
               ]
> [ FAQ: http://reality.sgi.com/austern_mti/std-
c++/faq.html              ]
>
>


Regards

Frank Z.


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Biju Thomas <b.thomas@attglobal.net>
Date: 2000/05/02
Raw View
fzhong@my-deja.com wrote:
>
> In article <3909C5DC.2C2C692F@attglobal.net>,
>   b.thomas@attglobal.net wrote:
> > fzhong@my-deja.com wrote:
> > >
> > > In article <38FDDBA5.BFD22617@attglobal.net>,
> > >   b.thomas@attglobal.net wrote:
> > >
> > > [snip...]
> > > > I am not following this case. You prove that the use of
> > > >
> > > >   template <class T> void f ( const T* const& );
> > > >
> > > > requires identity conversion. So does the use of
> > > >
> > > >   template <class T> void f ( const T& );
> > > >
> > > > Since the first is more specialized than the second, I would think
> > > that
> > > > the compiler will choose the first function "f(const T* const&)".
> But,
> > > > g++ chooses the second function "f(constT&)". So, how is g++
> correct
> > > in
> > > > this case?
> > >
> > > I think g++ is exactly correct.
> > > Partial ordering rule is only applied _after_
> > > the comparison of ICS(implicit conversion sequence)
> > > cannt resolve the call.
> >
> > Agreed.
> >
> > > in this case, (int*) to (int const* const&)
> > > is better to (int* const&)
> > >
>
> Oops, a typo here. it should be (int*) to
> (int* const&) is better than to (int const* const&)
>

I will appreciate if you can tell why it is so. My understanding is that
both require identity conversions.

  -- "int*" to "int* const&" is simple identity conversion
  -- "int*" to "const int* const&" is identity conversion because
     the parameter can be directly bound to the argument.

Our disagreement seems to hinge on the above claim. So, the question is,
is there anything wrong in the above conclusion that both require
identity conversions?

> >
> > So, you are saying that you disagree with the proof by Hyman Rosen
> that
> > both conversions are identity conversions. Here is what he said:
> >
> > "Given void f(const int *&) and int *p, the call f(p) results in
> > the parameter being directly bound to the argument,
>                       ^^^^^^^^^^^^^^^^^
>                          illegal, const int* and int* are not
>                          reference-compatible.

I believe that was a typo. The 'f2' functons in Scott's original example
are:

  template<typename T> void f2(const T* const&);
  template<typename T> void f2(const T&);

So, it really is "const int* const&".

> howerver, Given void f(const int * const&) and int *p, the call f(p)
> results in
> a temporary const int* pt initialized by p, then the parameter is
> bound to the temporary pt.
>

--
Biju Thomas

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Hyman Rosen <hymie@prolifics.com>
Date: 2000/04/26
Raw View
Biju Thomas <b.thomas@attglobal.net> writes:
> I am not following this case. You prove that the use of
>   template <class T> void f ( const T* const& );
> requires identity conversion. So does the use of
>   template <class T> void f ( const T& );
> Since the first is more specialized than the second, I would think that
> the compiler will choose the first function "f(const T* const&)". But,
> g++ chooses the second function "f(constT&)". So, how is g++ correct in
> this case?

Oops - I missed the fact that the f2 case had &s in both functions.
I'll look at this when I can get to my Standard copy again...

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: smeyers@aristeia.com (Scott Meyers)
Date: 2000/04/19
Raw View
I'm confused.  My goal is simple: I want to write two function templates,
one for pointer types, one for all other types:

  template<typename T> void f(const T*);    // should be instantiated and
                                            // called for pointer types

  template<typename T> void f(const T&);    // should be instantiated and
                                            // called for all other types

Alas, given this code,

  int *p;
  f(p);      // I want/expect f<int>(const int*) to be called

two current compilers (msvc, g++) call f<int*>(const int *&), and three
(bcc32, mwcc, and como) claim that the call is ambiguous.  That makes me 0
for 5.

To try to understand things better, I tinkered with the signature for the
function template for pointer types, and by adding an additional const or a
reference (in the code below), I was able to get the results indicated.

  #include <iostream>

  template<typename T> void f1(const T*) { std::cout << "T*" << '\n'; }
  template<typename T> void f1(const T&) { std::cout << "T&" << '\n'; }

  template<typename T> void f2(const T* const&) { std::cout << "T*" << '\n'; }
  template<typename T> void f2(const T&)        { std::cout << "T&" << '\n'; }

  template<typename T> void f3(const T* const) { std::cout << "T*" << '\n'; }
  template<typename T> void f3(const T&)       { std::cout << "T&" << '\n'; }


  int main()
  {
    int *ptrToInt = 0;
    f1(ptrToInt);         // T&:  msvc, g++
                          // Ambiguous: bcc32, mwcc, como

    f2(ptrToInt);         // T&:  msvc, g++, mwcc
                          // Ambiguous: bcc32
                          // Compiles: como (I don't know what happens
                          //   at runtime)

    f3(ptrToInt);         // T*:  mwcc
                          // T&:  msvc, g++
                          // Ambiguous: bcc32, como
  }

If somebody can explain these results to me, I'd really appreciate it.  The
fact that the results for f3 and f1 differ for mwcc especially surprise me,
because my understanding is that

  f(const T*);

and

  f(const T* const);

are the same signature.  Still, the rules for template instantiation are
not the same as the rules for overloading resolution, so it would not stun
me to learn that mwcc's behavior is correct.  I'd just like to understand
it.

My original motivation for this is to work differently with STL containers
containing pointers versus such containers containing non-pointers.  For
example, I'd like to be able to say

 for (containerType::iterator p = container.begin();
      p != container.end();
      ++p)
   f(*p);    // invoke the version of f appropriate for the type of
             // objects in the container

Coming up for workarounds isn't difficult, but I'd prefer to understand
what's going on rather than relying on workarounds.

All enlightenment will be appreciated,

Scott

--
"Effective STL" Seminar     June 7-9     Portland, Oregon
Details at http://www.trekservices.com/estl/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Hyman Rosen <hymie@prolifics.com>
Date: 2000/04/20
Raw View
smeyers@aristeia.com (Scott Meyers) writes:
> I'm confused.  My goal is simple: I want to write two function templates,
> one for pointer types, one for all other types:
>   template<typename T> void f(const T*);
>   template<typename T> void f(const T&);
> Alas, given this code,
>   int *p;
>   f(p);      // I want/expect f<int>(const int*) to be called
> two current compilers (msvc, g++) call f<int*>(const int *&), and three
> (bcc32, mwcc, and como) claim that the call is ambiguous.  That makes me 0
> for 5.

It's g++ that's correct, in all the cases that you tried. Here's why -

Given void f(const int *) and int *p, the call f(p) requires a
qualification conversion of its argument. This is according to
table 9 in 13.3.3.1.1/3.

Given void f(const int *&) and int *p, the call f(p) results in
the parameter being directly bound to the argument, according to
8.5.3. According to 13.3.3.1.4, this causes the implicit conversion
sequence for the call to be the identity conversion.

Finally, according to 13.3.3.2/3, standard conversion sequence S1
is better than another S2 if S1 is a proper subsequence of S2. Also
in the same paragraph, we have that the identity conversion is a
subsequence of any non-identity conversion.

Therefore the refernce binding is better.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Dennis Yelle <dennis51@jps.net>
Date: 2000/04/20
Raw View
Hyman Rosen wrote:
>
> smeyers@aristeia.com (Scott Meyers) writes:
> > I'm confused.  My goal is simple: I want to write two function templates,
> > one for pointer types, one for all other types:
> >   template<typename T> void f(const T*);
> >   template<typename T> void f(const T&);
> > Alas, given this code,
> >   int *p;
> >   f(p);      // I want/expect f<int>(const int*) to be called
> > two current compilers (msvc, g++) call f<int*>(const int *&), and three
> > (bcc32, mwcc, and como) claim that the call is ambiguous.  That makes me 0
> > for 5.
>
> It's g++ that's correct, in all the cases that you tried. Here's why -
>
> Given void f(const int *) and int *p, the call f(p) requires a
> qualification conversion of its argument. This is according to
> table 9 in 13.3.3.1.1/3.
>
> Given void f(const int *&) and int *p, the call f(p) results in
> the parameter being directly bound to the argument, according to
> 8.5.3. According to 13.3.3.1.4, this causes the implicit conversion
> sequence for the call to be the identity conversion.
>
> Finally, according to 13.3.3.2/3, standard conversion sequence S1
> is better than another S2 if S1 is a proper subsequence of S2. Also
> in the same paragraph, we have that the identity conversion is a
> subsequence of any non-identity conversion.
>
> Therefore the refernce binding is better.

That may be what the current standard says,
but is it what we really want?

According to GCC, this works:

  template<typename T>
  int f( T*&) { return printf( "T*&\n"); }

  template<typename T>
  int f(const T&) { return printf( "const T&\n"); }

  int *p;
  int i = f(p);      // I want/expect T*& to be printed.

But this does not:

  template<typename T>
  int f( const T*&) { return printf( "const T*&\n"); }

  template<typename T>
  int f(const T&) { return printf( "const T&\n"); }

  int *p;
  int i = f(p);      // I want/expect const T*& to be printed.

I don't really want the addition of the word "const" there
to have that much power.  But it does.

Is there any way to fix this without breaking too much other stuff?

Dennis Yelle

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Biju Thomas <b.thomas@attglobal.net>
Date: 2000/04/20
Raw View
Hyman Rosen wrote:
>
> It's g++ that's correct, in all the cases that you tried. Here's why -
>
> Given void f(const int *) and int *p, the call f(p) requires a
> qualification conversion of its argument. This is according to
> table 9 in 13.3.3.1.1/3.
>

Agreed.

> Given void f(const int *&) and int *p, the call f(p) results in
> the parameter being directly bound to the argument, according to
> 8.5.3. According to 13.3.3.1.4, this causes the implicit conversion
> sequence for the call to be the identity conversion.
>
> Finally, according to 13.3.3.2/3, standard conversion sequence S1
> is better than another S2 if S1 is a proper subsequence of S2. Also
> in the same paragraph, we have that the identity conversion is a
> subsequence of any non-identity conversion.
>
> Therefore the refernce binding is better.
>

I am not following this case. You prove that the use of

  template <class T> void f ( const T* const& );

requires identity conversion. So does the use of

  template <class T> void f ( const T& );

Since the first is more specialized than the second, I would think that
the compiler will choose the first function "f(const T* const&)". But,
g++ chooses the second function "f(constT&)". So, how is g++ correct in
this case?

--
Biju Thomas

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 2000/04/20
Raw View
On Wed, 19 Apr 2000 02:18:32 CST, Scott Meyers <smeyers@aristeia.com> wrote:

>I'm confused.  My goal is simple: I want to write two function templates,
>one for pointer types, one for all other types:
>
>  template<typename T> void f(const T*);    // should be instantiated and
>                                            // called for pointer types
>
>  template<typename T> void f(const T&);    // should be instantiated and
>                                            // called for all other types

As f(const T*) is a specialization of f(const T&), I think f(const T&)
should be declared first and f(const T *) next.  But then again, there's
a distinction between function overloading and function specialization
that I don't really understand, so maybe what you wrote is fine.


>Alas, given this code,
>
>  int *p;
>  f(p);      // I want/expect f<int>(const int*) to be called

Ok.  But I see that we're going to get into all sorts of trouble with
top level and first level consts, so why not create two more functions
   template<typename T> void f(T *) { return f<const T>(p); }
   template<typename T> void f(T&) { return f<const T>(p); }
These forwarding functions express what we want directly and skirt
(at least that's what I think) the errors we have below.


>two current compilers (msvc, g++) call f<int*>(const int *&), and three
>(bcc32, mwcc, and como) claim that the call is ambiguous.  That makes me 0
>for 5.

Regarding g++ and MSVC:
I don't think any compiler can call f<int *>(int const * &).
Perhaps it is f<int *>(int *const &).

Hint: when dealing with const values or const pointers or const pointer
pointers, write "const T" as "T const".  Thence we'll have the rule
that const protects whatever is to its left.  Which makes it easier for
us to analyze things like "T const *const * * *const".

Recall the basic example --
   typedef char * String;
   const String string="hello world";
   string[0]=toupper(string[0]); // compile error?
We expect a compile error because the type of string is, or we think it
is, "const char * string".  Had we written
   String const string="hello world";
we would see that the type of string is "char *const string", which is
completely different.



I think g++, which calls the second f(T const &) with T=="int const *", is
right because it is a direct match.  The first f(T const *) with T=="int"
requires a conversion of 'p' in the calling code from "int *" to
"int const *" -- ie, we add a first level const.  (By contrast, adding a
top level or zero level const -- eg, "int *" to "int *const" -- is
considered zero conversions.)

I think EDG, which claims the construct is an error because both f match
the call, is also right because adding a first level const is so basic
that it could be considered zero conversions, and thence either f is
right.


Solutions:

1. Turn 'p' into a "int const * p".  This should unambiguously call
f(const T *) with T==int.

2. Specify the template argument in the function call "f<int>(p)"
which calls f(const T *) with T==int.

3. Write the forwarding functions as I did above and just call "f(p)".
This calls f(T *) with T==int, which then calls f(const T2 *) with
T2=="const T".

Note that if we choose this solution (3) and inline it, we end up with
solution (2).  But solution (3) is better because it frees the calling
code from messy template arguments.



>To try to understand things better, I tinkered with the signature for the
>function template for pointer types, and by adding an additional const or a
>reference (in the code below), I was able to get the results indicated.
>
>  #include <iostream>
>
>  template<typename T> void f1(const T*) { std::cout << "T*" << '\n'; }
>  template<typename T> void f1(const T&) { std::cout << "T&" << '\n'; }
>
>  template<typename T> void f2(const T* const&) { std::cout << "T*" << '\n'; }
>  template<typename T> void f2(const T&)        { std::cout << "T&" << '\n'; }
>
>  template<typename T> void f3(const T* const) { std::cout << "T*" << '\n'; }
>  template<typename T> void f3(const T&)       { std::cout << "T&" << '\n'; }

We have all sorts of redefinition errors here.  I suppose you made 3
different programs.


>
>  int main()
>  {
>    int *ptrToInt = 0;
>    f1(ptrToInt);         // T&:  msvc, g++
>                          // Ambiguous: bcc32, mwcc, como

See the discussion above.
I think g++ is right according to the standard, and EDG is right according
to what-is-reasonable.

>    f2(ptrToInt);         // T&:  msvc, g++, mwcc
>                          // Ambiguous: bcc32
>                          // Compiles: como (I don't know what happens
>                          //   at runtime)

Because f(T *) and f(T *const &) are conceptually equivalent, I have the
same answer as above.


>    f3(ptrToInt);         // T*:  mwcc
>                          // T&:  msvc, g++
>                          // Ambiguous: bcc32, como

Because f(T *) and f(T *const) are equivalent, I have the same answer as
above.

--
--------------
siemel b naran
--------------

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@germany.sun.com>
Date: 2000/04/21
Raw View
Am 20.04.00, 11:15:21, schrieb Dennis Yelle <dennis51@jps.net> zum Thema
Re: Instantiation Clarification Sought:

> but is it what we really want?

> According to GCC, this works:

>   template<typename T>
>   int f( T*&) { return printf( "T*&\n"); }
[SNIP]

> But this does not:

[***]
>   template<typename T>
>   int f( const T*&) { return printf( "const T*&\n"); }


>   template<typename T>
>   int f(const T&) { return printf( "const T&\n"); }

>   int *p;
>   int i = f(p);      // I want/expect const T*& to be printed.

> I don't really want the addition of the word "const" there
> to have that much power.  But it does.

It depends on where you add const:

 template<typename T>
 int f( T* const&) { return printf( "T* const&\n"); }

This works as you would like it to work.
But the part marked [***] should never work. Even

 int *p;
 const int*&rpc = p; // error

does not work (for well known reasons).

> Is there any way to fix this without breaking too much other stuff?

No. I don't think disregarding non-toplevel const would be a good thing.

And what is intuitive is in the eye of the beholder. I for instance find
it more intuive that f(int*&) is a better match than f(int const *) or
f(int const * const&) if the argument is an 'int*'. This may be a problem
if the int* is not an lvalue. So we might consider implicitly adding a
top-level const to rvalues in template argument deduction and/or overload
resolution.
But this could probably break existing code and the rules for template
argument deduction and overload resolution are complicated already. So I
don't think that is a good idea either.

If you provide overloads (especially in the context of function template
partial ordering) you just have to think about how these work together.
You should think explicitly about the behaviour in borderline cases. If
you rely on a particular behaviour in such cases, another overload that
targets this cases explicitly may be necessary (could be an inline
one-liner that forwards to one of the other overloads).
In this case I think the new overload even improves readability and
maintainabilty, as it makes your intent explicit. It also prevents
problems with compilers that don't handle this properly.

-- J   rg Barfurth


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]