Topic: Specializing templates for [const] char* pointers and string literals


Author: "Jeff Peil" <jpeil@bigfoot.com>
Date: Fri, 16 Mar 2001 01:37:49 GMT
Raw View
"Scott Meyers" <smeyers@aristeia.com> wrote in message
news:MPG.151ab8dc7c832e2f9897a2@news.supernews.com...
> On Thu, 15 Mar 2001 17:51:38 GMT, Anatoli wrote:
> > My understanding is that "S1" is not a specialization
> > of "G", but a different function template altogether.
> > "G" requires two const references, and specializations
> > of "G" can only vary types of these references.
>
> Ah.  Where in the standard is this specified, i.e., that if the general
> template takes a const T&, all specializations must take a const U& where
U is
> more specialized than T?

I think it's implicit in 14.5.5.2 item 4: "Using the transformed function
parameter list, perform argument deduction against the other function
tem-plate.
The transformed template is at least as specialized as the other if, and
only if, the deduction succeeds and the deduced parameter types are an exact
match (so the deduction does not rely on implicit conver-sions)."

If G is not a specialization of S1 and S1 is not a specialization of G, then
I think all that can be said is that they are overloaded templates (and if
both are in scope they will always resolve ambiguously.)  By changing S1 &
S2 to be const references to const char* and char* respectively you then
reach the point where per item 4 S1&S2 are "at least as specialized" as G
while G is not "at least as specialized" as S1&S2 which means that per item
5, S1&S2 are now specialized than G.

Perhaps this should be more explicit in the standard, and is worthy of a
defect report?


---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Kenny Simpson <theonetruekenny@yahoo.com>
Date: Mon, 19 Mar 2001 00:09:40 GMT
Raw View
Greg Comeau wrote:

> In article <MPG.15186d2e7139b7a998979f@news.supernews.com>,
> ...snip...
 >

> It might make sense to look at the A like so:
>
>   void foo(const char *);
>   void foo(const char (&)[4]);
>   int main()
>   {
>     foo("abc");
>   }

   Ok, but the first foo is generated from a template more specialized
than the template used to generate the second foo.  Therefore, the more
specialized version should be used unambiguously.
   Did I miss something?

-Kenny

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Mon, 19 Mar 2001 00:09:19 GMT
Raw View
First, I want to thank Andrea for working so hard to try to make sense of
this problem.  On a lark, I looked up unresolved issues with 14.5.5.2, and
there are two, #23 and #214.  The latter looks especially relevant, and it
leads me to believe that there may not be a definitive answer to my
question.  Nevertheless, let's push ahead.

On Fri, 16 Mar 2001 21:34:45 GMT, Andrea Ferro wrote:
> I'll try to elaborate it according to my understanding of the standard,
> however note that I'm not totally sure I got 14.5.5.2 wording right.

Based on issue #214, it's not clear that anybody got the wording right :-)

> G has two type template parameters. According to sub 3 we are to
> synthesize two unique types. I'll call them __T1 and __T2. Then the
> transformed parameter list is to be used for deduction for template
> S1. The deduction, in this case, fails (the second parameter will never
> match). Therefore G is not as specialized as S1.

Can you please be more explicit about what you're doing here?  Here's G:

  template<typename T1, typename T2>
  void f(const T1&, const T2&);           // G

What is the transformed G?  Does it look like this?

  void f(const __T1&, const __T2&);       // transformed G?

If so, what does it mean to "perform argument deduction against S1"?  How
can we do argument deduction from a function declaration?

Or is the transformed G supposed to be a call:

  f(__T1, __T2);                         // transformed G?

But para 2 suggests that transformations are applied to *templates*, so
perhaps the transformed G is another template.  If so, I have no idea what
it looks like...

> This, of course, if I get the standard right. Anyone to confirm (or confute)?

Please, please, please, others do chime in with opinions!

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrea Ferro" <AndreaF@UrkaDVD.it>
Date: Mon, 19 Mar 2001 16:46:13 GMT
Raw View
"Scott Meyers" <smeyers@aristeia.com> wrote in message
news:MPG.151cab03ddf4238c9897a5@news.supernews.com...
> First, I want to thank Andrea for working so hard to try to make sense of
> this problem.  On a lark, I looked up unresolved issues with 14.5.5.2, and
> there are two, #23 and #214.  The latter looks especially relevant, and it
> leads me to believe that there may not be a definitive answer to my
> question.  Nevertheless, let's push ahead.

Thanks for pointing me to those. Yep, the standard looks foggy to someone else
too :-)

> On Fri, 16 Mar 2001 21:34:45 GMT, Andrea Ferro wrote:
> > I'll try to elaborate it according to my understanding of the standard,
> > however note that I'm not totally sure I got 14.5.5.2 wording right.
>
> Based on issue #214, it's not clear that anybody got the wording right :-)

Well ...

> > G has two type template parameters. According to sub 3 we are to
> > synthesize two unique types. I'll call them __T1 and __T2. Then the
> > transformed parameter list is to be used for deduction for template
> > S1. The deduction, in this case, fails (the second parameter will never
> > match). Therefore G is not as specialized as S1.
>
> Can you please be more explicit about what you're doing here?  Here's G:
>
>   template<typename T1, typename T2>
>   void f(const T1&, const T2&);           // G
>
> What is the transformed G?  Does it look like this?
>
>   void f(const __T1&, const __T2&);       // transformed G?

Yes. Sort of. But it is not the template that is transformed. It's the parameter
list. Remember we are not in the middle of code. We are inside a compiler
"function" looking for a partial order for oveloaded template functions. The
transformed parameter list looks like that if __T1 and __T2 are "synthesized
unique types". They could have been called T1 and T2, but since these do have a
meening in the code we are compiling, I delibratelly changed the names!

> If so, what does it mean to "perform argument deduction against S1"?  How
> can we do argument deduction from a function declaration?

A parameter list, remember? We try argument deduction for S1 with the
transformed parameter list.
Sort of:

class __T1 {};
class __T2 {};

template<typename T>         // S1
void f(const T&, const char*);

...
    const __T1 &t1;
    const __T2 &t2;

    f(t1,t2);

However note that this tecnique fails in other circumstances. For example if we
match S1 to G.

class __T {};

template<typename T1, typename T2>   // G
void f(const T1&, const T2&);

...
    const __T &t;
    const char *p;

    f(t,p);

Here is where things are not clear. The problem is that with this code "p" is an
l-value while the original S1 parameter was not. We may change it like this:

class __T {};
const __T& __arg1();
const char* __arg2();

template<typename T1, typename T2>   // G
void f(const T1&, const T2&);

...
    f(__arg1(),__arg2());

however, as issues have noted, it is not clear exactly if this is correct. WHat
kind of "argiment deduction" is due? In this code, for example, a temporary may
be generated for the second parameter since it is a constant reference! Also are
T and const T& an exact match?

Looking from the point of view of a compiler and, maybe, trying to interpret
what was ment in the standard, we cannot write "code" that actually behaves like
our "deduction". We have a list of types. Since they are all call arguments they
are all r-values unless intrinsecally l-values (that is references). We need to
"build" a matching list for the target template. The two lists must be an exact
match. I think "exact match" is one way: l-value to r-value is an exact match
but vice-versa is nonsense (and we should, IMHO, not be allowed to always match
a "const&" with the "temporary trick": we are sorting templates, not forcing a
meaningfull call mechanism).

Is this blurred? Foggy? Yes! It is. But if a was to write a compiler right now,
waiting for a clarification from the comittee, that seems the most sensible way
to do it!

> Or is the transformed G supposed to be a call:
>
>   f(__T1, __T2);                         // transformed G?

I considered this option. But if it was that way than the whole idea of using
"parameter list" for partial ordering would collapse. Really just the number of
arguments would be relevant. And carefull reading sub 3 makes clear this is not
the case: syntesize from template parameter and substitute in the function
parameter list.

> But para 2 suggests that transformations are applied to *templates*, so
> perhaps the transformed G is another template.  If so, I have no idea what
> it looks like...

No. What is transformed is a parameter list. Not a template. And sub 2 makes
clear what is the reason of this mechanism: partial ordering of overloaded
template functions. If we have not two overloaded template functions this whole
process is nonsense.

> > This, of course, if I get the standard right. Anyone to confirm (or
confute)?
>
> Please, please, please, others do chime in with opinions!

Eventually someone from the core group. Someone lurking this NG. Or maybe we
should be cross-posting to c++ moderated?

--

Andrea Ferro

---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info http://www.brainbench.com/transcript.jsp?pid=2522556



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrea Ferro" <AndreaF@UrkaDVD.it>
Date: Tue, 27 Mar 2001 07:03:05 CST
Raw View
"Scott Meyers" <smeyers@aristeia.com> wrote in message
news:MPG.15267f9960657fd09897ab@news.supernews.com...
> On Mon, 19 Mar 2001 16:46:13 GMT, Andrea Ferro wrote:
> >     const __T &t;
> >     const char *p;
> >
> >     f(t,p);
> >
> > Here is where things are not clear. The problem is that with this code
> > "p" is an l-value while the original S1 parameter was not.
>
> What leads you to this conclusion?  An object of type const char* might be
> an lvalue or it might be an rvalue.  Or do you mean that the original S1
> allowed rvalues to be passed into its const char* parameter, hence we must
> consider it to be an rvalue?

Precisely! See, it is my opinion that in the context of function call, that is
when binding parameters, you convert each argument expression to an r-vaule. If
it's an r-value that's it. If it is an l-value, it is converted with the l to r
value conversion. The only exception may be the binding to a non const
reference. This is not really an exception if you see it from the point of view
of the compiler (the compiler may consider it as passing an adress to non const
object, and 'the address' is the r-value here).

Now it makes sense that when you do template parameter deduction from a function
parameter list (the case in 14.5.5.2 sub 4) you assume the types to be r-values
(with the only possible exception of a non const reference).

> > We may change it like this:
> >
> > class __T {};
> > const __T& __arg1();
> > const char* __arg2();
> >
> > template<typename T1, typename T2>   // G
> > void f(const T1&, const T2&);
> >
> > ...
> >     f(__arg1(),__arg2());
> >
> > however, as issues have noted, it is not clear exactly if this is
> > correct. WHat kind of "argiment deduction" is due? In this code, for
> > example, a temporary may be generated for the second parameter since it
> > is a constant reference! Also are T and const T& an exact match?
>
> I don't see any way to interpret the standard such that T and const T& are
> not an exact match.  You continue as follows:

I was unclear. I mean, can I "convert" an r-value of type T to a const T&? IMHO
That "conversion" is not a conversion: it is part of the binding process.

> > Looking from the point of view of a compiler and, maybe, trying to
> > interpret what was ment in the standard, we cannot write "code" that
> > actually behaves like our "deduction". We have a list of types. Since
> > they are all call arguments they are all r-values unless intrinsecally
> > l-values (that is references). We need to "build" a matching list for the
> > target template. The two lists must be an exact match. I think "exact
> > match" is one way: l-value to r-value is an exact match but vice-versa is
> > nonsense (and we should, IMHO, not be allowed to always match a "const&"
> > with the "temporary trick": we are sorting templates, not forcing a
> > meaningfull call mechanism).
>
> But the standard is clear that we are using the same mechanism as is used
> for overloading resolution.  The term "exact match" is defined only in
> 13.3.3.1.1, and paragraph 4 of 14.5.5.2 says
>
>   The transformed template is at least as specialized as the other if, and
>   only if, the deduction succeeds and the deduced parameter types are an
>   exact match (so the deduction does not rely on implicit conversions).
>
> The meaning of "exact match" as "preventing implicit conversions" is
> completely consistent with 13.3.3.1.1.

I think "partial ordering" is supposed to work "within" the process of resolving
an overload. Once we transformed one template function parameter list we use
this to deduct the other tamplate arguments. This is not the process of
resolving overloads but that of deducing template arguments. That is 14.8.2 but,
unless I missed something, there's nothing explicit there for this case. I mean:
am I right in considering the "types" coming from an argument list as r-valued
only (with the cited "almost special" case of a non const reference) ? There's
nothing in 14.8.2 about that. If I'm right than the code that will force the
compiler to do the most similar deduction is that with function calls above:

f(__arg1(),__arg2());

However this may deduct a "const T&" reference for an r-value of type T. Now the
question again is: can we consider this an exact match? I think not. It is
possible to pass an r-value of type T when the parameter is a const T& but there
will be no conversion: it will be done with a special mechanism described as
part of reference binding. But "exact match" is a conversion rank as for
13.3.3.1.1, and from "r-value of type T" to "constant reference to T" I see no
conversion at all. They are just two different types. They happen to be
compatible in the context of a function call because of the reference binding,
but there's no conversion between them.

> > Is this blurred? Foggy? Yes! It is. But if a was to write a compiler
> > right now, waiting for a clarification from the comittee, that seems the
> > most sensible way to do it!
>
> Based on the results of several compilers on the original test program,
> most compiler implementers fail to see it your way.  As Mumit Khan posted,
> here are the current relevant results.  Recall that pc is of type const
> char* and "A" means the call is flagged as ambiguous.
>
>                   //  VC   BCC  MWCW g++ Comeau WS5  WS6u1 g++-3
>                   //  --- ----- ---- --- ------ ---- ----- -----
>   f(x, pc);       //   S1   S1    A   A    S1    S1    S1    S1
>   f(x, "abc");    //   A    S2    A   A    A     A     S1    S1
>
> Interestingly, g++ 2.95.2 yields A while g++ 2.97 yields S1.  If this
> reflects a conscious decision on the part of the g++ developers, that
> leaves everybody except MWCW choosing S1.

That is puzzling. I think the string is to be considered constant and therefore
the two calls should give the same results. This rules out half of the
compilers! Then I think A is the correct outcome because I do not consider
either template as more specialized: both transformed parameter lists, IMO, fail
to deduce the other template with exact matches.

>
> BTW, could a VC7 beta user please post VC7b1 results?
>

As you know I also follow the MS beta NG. You posted there but seem to have left
thereafter. There's been a post from my side on the thread you started about VC7
behaviour with your code. Specifically VC7 will not be 100% conformant, there
are some well known major areas the VC team did not implement yet and will not
for this release. Among those is partial ordering. Therefore VC (6 or 7) is
known not to be compliant on this.


Andrea Ferro

---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info http://www.brainbench.com/transcript.jsp?pid=2522556



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Wed, 21 Mar 2001 19:11:25 GMT
Raw View
On Mon, 19 Mar 2001 02:42:56 GMT, James Kuyper Jr. wrote:
> // f2 wraps  f1
>   template<typename T1, typename T2>
>   void f1(const T1&, const T2&);           // G
>
>   template<typename T1>                   // S1
>   void f2(const T1&t, const char* p)
>  { f1(t,p);}
> ==================================================
> // f1 wraps f2
>   template<typename T1>                   // S1
>   void f2(const T1&t, const char* p);
>
>   template<typename T1, typename T2>
>   void f1(const T1&t1, const T2&t2)           // G
>  { f2(t1, t2); }
>
>
> If, ignoring the return types, one function template can wrap another
> for arbitrary template arguments, and the deduced template parameters
> for the wrapped function call are always an exact match, then the
> wrapped template is at least as specialized as the wrapping template.

If I understand you, you believe that S1 can successfully wrap G, but G
can't successfully wrap S1, so S1 is more specialized than G.  You also
believe that this call

  int i;
  const char *pcc;

  f(i, pcc);

should cause S1 to be instantiated without ambiguity.  Is that a correct
summary of your beliefs?

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrea Ferro" <AndreaF@UrkaDVD.it>
Date: Fri, 23 Mar 2001 20:42:25 GMT
Raw View
"James Kuyper Jr." <kuyper@wizard.net> wrote in message
news:3AB9855B.CD566791@wizard.net...
> Scott Meyers wrote:
> >
> > On Mon, 19 Mar 2001 02:42:56 GMT, James Kuyper Jr. wrote:
> > > // f2 wraps  f1
> > >   template<typename T1, typename T2>
> > >   void f1(const T1&, const T2&);           // G
> > >
> > >   template<typename T1>                   // S1
> > >   void f2(const T1&t, const char* p)
> > >       { f1(t,p);}
>
> According to Andrea Ferro, this is prohibited because it involves
> "trying to match "const char*" as an r-value" to an lvalue. However,
> that's a problem only when the reference is non-const, which doesn't
> apply in this case. Per 13.3.3.1.4, that binding counts as an identity
> conversion, and thus as an exact match.

Not sure. In the code above "p" is not an r-value. It is an l-value. The call to
f1 is totally correct. When speculating on r-value to l-value conversions I was
referring to 14.5.5.2 sub 4 where argument deduction is performed not from a
variable like 'p' but from an argument list. And that may well include r-values.
Now the question is wether converting an r-value of type T to a constant
reference to the same type (const T&) can be considered an exact match. I think
not because an exact match is defined for a "conversion". The case of r-value to
const reference is not really a conversion, in the standard. It is considered a
"reference binding". It is my opinion that defining a "partial order" based on
the parameter types really just work on the parameter types themself, eventually
considering type conversion, but it does not borrow from the realm of the
argument binding. That is, the types and the type conversions are considered but
mechanisms used to bind an argument to a parameter and make up the actual code
that performs the call are not. And AFAIK when you call a function expecting a
const reference and you only have an r-value you do not actually "convert" an
r-value to an l-value to take a reference of, but you DO "create" (if the
r-value is not the kind of r-values that is implicitelly represented by an
object) a hidden temporary (an l-value, indeed) as part of the "reference
binding" process.

Note that 13.3.3.1.1 sub 3 wordings seem to imply that the conversion rank
computation already accounts for reference binding. It is my opinion, however,
that r-value to const reference is not a conversion at all. What that wording is
referring to is the eventual conversion needed for one of the direct reference
bindings, conversion done according to 13.3.1.6.

> > > ==================================================
> > > // f1 wraps f2
> > >   template<typename T1>                   // S1
> > >   void f2(const T1&t, const char* p);
> > >
> > >   template<typename T1, typename T2>
> > >   void f1(const T1&t1, const T2&t2)           // G
> > >       { f2(t1, t2);   }
> > >
> > >
> > > If, ignoring the return types, one function template can wrap another
> > > for arbitrary template arguments, and the deduced template parameters
> > > for the wrapped function call are always an exact match, then the
> > > wrapped template is at least as specialized as the wrapping template.

Note that I absolutelly disagree on this point. Exactly because the l vs. r
"valueness" is not handled correctly.


Andrea Ferro

---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info http://www.brainbench.com/transcript.jsp?pid=2522556



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Sun, 25 Mar 2001 15:07:06 GMT
Raw View
On Mon, 19 Mar 2001 16:46:13 GMT, Andrea Ferro wrote:
>     const __T &t;
>     const char *p;
>
>     f(t,p);
>
> Here is where things are not clear. The problem is that with this code
> "p" is an l-value while the original S1 parameter was not.

What leads you to this conclusion?  An object of type const char* might be
an lvalue or it might be an rvalue.  Or do you mean that the original S1
allowed rvalues to be passed into its const char* parameter, hence we must
consider it to be an rvalue?

> We may change it like this:
>
> class __T {};
> const __T& __arg1();
> const char* __arg2();
>
> template<typename T1, typename T2>   // G
> void f(const T1&, const T2&);
>
> ...
>     f(__arg1(),__arg2());
>
> however, as issues have noted, it is not clear exactly if this is
> correct. WHat kind of "argiment deduction" is due? In this code, for
> example, a temporary may be generated for the second parameter since it
> is a constant reference! Also are T and const T& an exact match?

I don't see any way to interpret the standard such that T and const T& are
not an exact match.  You continue as follows:

> Looking from the point of view of a compiler and, maybe, trying to
> interpret what was ment in the standard, we cannot write "code" that
> actually behaves like our "deduction". We have a list of types. Since
> they are all call arguments they are all r-values unless intrinsecally
> l-values (that is references). We need to "build" a matching list for the
> target template. The two lists must be an exact match. I think "exact
> match" is one way: l-value to r-value is an exact match but vice-versa is
> nonsense (and we should, IMHO, not be allowed to always match a "const&"
> with the "temporary trick": we are sorting templates, not forcing a
> meaningfull call mechanism).

But the standard is clear that we are using the same mechanism as is used
for overloading resolution.  The term "exact match" is defined only in
13.3.3.1.1, and paragraph 4 of 14.5.5.2 says

  The transformed template is at least as specialized as the other if, and
  only if, the deduction succeeds and the deduced parameter types are an
  exact match (so the deduction does not rely on implicit conversions).

The meaning of "exact match" as "preventing implicit conversions" is
completely consistent with 13.3.3.1.1.

> Is this blurred? Foggy? Yes! It is. But if a was to write a compiler
> right now, waiting for a clarification from the comittee, that seems the
> most sensible way to do it!

Based on the results of several compilers on the original test program,
most compiler implementers fail to see it your way.  As Mumit Khan posted,
here are the current relevant results.  Recall that pc is of type const
char* and "A" means the call is flagged as ambiguous.

                  //  VC   BCC  MWCW g++ Comeau WS5  WS6u1 g++-3
                  //  --- ----- ---- --- ------ ---- ----- -----
  f(x, pc);       //   S1   S1    A   A    S1    S1    S1    S1
  f(x, "abc");    //   A    S2    A   A    A     A     S1    S1

Interestingly, g++ 2.95.2 yields A while g++ 2.97 yields S1.  If this
reflects a conscious decision on the part of the g++ developers, that
leaves everybody except MWCW choosing S1.

BTW, could a VC7 beta user please post VC7b1 results?

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: comeau@panix.com (Greg Comeau)
Date: Wed, 14 Mar 2001 17:30:37 GMT
Raw View
In article <MPG.15186d2e7139b7a998979f@news.supernews.com>,
Scott Meyers  <smeyers@aristeia.com> wrote:
>Given two types T1 and T2, I can write a template for performing some
>function f on objects of those types:
>
>  template<typename T1, typename T2>
>  void f(const T1&, const T2&);                // general template (G)
>
>When T2 is const char*, char*, or a string literal (const char[]), I have a
>more efficient way to perform f, so I'd like to overload f accordingly:
>
>  template<typename T1>                   // specialized template 1 (S1)
>  void f(const T1&, const char*);         // for T2 == const char* or
>                                          // T2 == a string literal
>
>  template<typename T1>                   // specialized template 2 (S2)
>  void f(const T1&, char*);               // for T2 == char*
>
>Given these templates, I expect the following calls to behave like this:
>
>  int x, y;
>  const char *pc;
>  char *p;
>                  // Expected
>                  // --------
>  f(x, y);        //     G
>  f(x, pc);       //     S1
>  f(x, "abc");    //     S1
>  f(x, p);        //     S2
>
>That's not what I get.  I submitted the above to VC 6.4, BCC 5.1.1, MWCW
>6.1, g++ 2.95.2, and Como 4.2.44, and these are the results.  An entry of
>"A" means the compiler rejected the call as being ambiguous, and a "-"
>means there was no ambiguity, but I don't know which template was
>instantiated/called.  (My only access to Como is at the Comeau web site,
>and that offers only compiling, not execution.)
>
>                  //  VC   BCC  MWCW g++ Como
>                  //  --- ----- ---- --- ----
>  f(x, y);        //   G    G     G   G    -
>  f(x, pc);       //   S1   S1    A   A    -
>  f(x, "abc");    //   A    S2    A   A    A
>  f(x, p);        //   S2   S2    A   A    -
>
>The situation with the third call is tricky, because the type of "abc" is
>const char[4], not const char*.  I thought there was an outstanding issue
>about adding conversion from const char[n] to const char* during template
>argument deduction, but now I can't find it.  Anyway, since none of the
>above compilers agree with my expectation, there's clearly something wrong
>with my expectation.  But the compilers  also fail to agree with one
>another.  I'd like to know two things:
>  - What set of templates for f should, collectively, give me the behavior
>    I want?  That is, how can I specialize f's behavior when T2 is
>    a string literal or [const] char* pointer?
>  - Which, if any, compilers accept the "correct" solution?

como ("Comeau C++") results are: G, S1, A, S2
It might make sense to look at the A like so:

  void foo(const char *);
  void foo(const char (&)[4]);
  int main()
  {
    foo("abc");
  }
--
Greg Comeau                   Comeau C/C++ 4.2.45 "so close"
ONLINE COMPILER ==>           http://www.comeaucomputing.com/tryitout
4.2.45.2 during March!        NEW ONLINE: Try out our C99 mode!
comeau@comeaucomputing.com    http://www.comeaucomputing.com

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Wed, 14 Mar 2001 18:54:55 GMT
Raw View
On Wed, 14 Mar 2001 17:30:37 GMT, Greg Comeau wrote:
> It might make sense to look at the A like so:
>
>   void foo(const char *);              // 1
>   void foo(const char (&)[4]);         // 2
>   int main()
>   {
>     foo("abc");
>   }

I've numbered the foos above.  Why isn't 2 preferred?  Back in the days of
the ARM, both 1 and 2 would be exact matches, but calling 1 would require a
trivial conversion from const T[] to const T*.  2 would require no such
trivial conversion.  I know that both the terminology and the rules have
changed since the ARM days, so are both calls above now equally good?

And please don't overlook the big question regarding my entire original
question.  What is correct standard-conformant behavior, and does anybody
exhibit it?  Here's the current table of results:

                  //  VC   BCC  MWCW g++ Comeau
                  //  --- ----- ---- --- ------
  f(x, y);        //   G    G     G   G    G
  f(x, pc);       //   S1   S1    A   A    S1
  f(x, "abc");    //   A    S2    A   A    A
  f(x, p);        //   S2   S2    A   A    S2

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Victor Bazarov" <vAbazarov@dAnai.com>
Date: Wed, 14 Mar 2001 20:22:02 GMT
Raw View
"Scott Meyers" <smeyers@aristeia.com> wrote...
> On Wed, 14 Mar 2001 17:30:37 GMT, Greg Comeau wrote:
> > It might make sense to look at the A like so:
> >
> >   void foo(const char *);              // 1
> >   void foo(const char (&)[4]);         // 2
> >   int main()
> >   {
> >     foo("abc");
> >   }
>
> I've numbered the foos above.  Why isn't 2 preferred?  Back in the
days of
> the ARM, both 1 and 2 would be exact matches, but calling 1 would
require a
> trivial conversion from const T[] to const T*.  2 would require no
such
> trivial conversion.  I know that both the terminology and the rules
have
> changed since the ARM days, so are both calls above now equally good?

You're may be right, according to 13.3.3.2, paragraph 3 the identity
conversion is a subsequence of any other implicit conversion, and as
such is _better_ than any other implicit conversion (array-to-pointer
included).  But that's how I read it...

Victor
--
Please remove capital A's from my address when replying by mail



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: khan@janus.xraylith.wisc.edu (Mumit Khan)
Date: Wed, 14 Mar 2001 23:55:42 GMT
Raw View
In article <MPG.15195e186b122c2b9897a0@news.supernews.com>,
Scott Meyers  <smeyers@aristeia.com> wrote:

>Here's the current table of results:
>
>                  //  VC   BCC  MWCW g++ Comeau
>                  //  --- ----- ---- --- ------
>  f(x, y);        //   G    G     G   G    G
>  f(x, pc);       //   S1   S1    A   A    S1
>  f(x, "abc");    //   A    S2    A   A    A
>  f(x, p);        //   S2   S2    A   A    S2
>

Add Sun WS5, WS6 update 1 and gcc-2.97 (3.0 development branch):

                  //  VC   BCC  MWCW g++ Comeau WS5  WS6u1 g++-3
                  //  --- ----- ---- --- ------ ---- ----- -----
  f(x, y);        //   G    G     G   G    G     G     G     G
  f(x, pc);       //   S1   S1    A   A    S1    S1    S1    S1
  f(x, "abc");    //   A    S2    A   A    A     A     S1    S1
  f(x, p);        //   S2   S2    A   A    S2    S2    S2    S2

Regards,
Mumit

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Jeff Peil" <jpeil@bigfoot.com>
Date: Thu, 15 Mar 2001 01:02:13 GMT
Raw View
"Scott Meyers" <smeyers@aristeia.com> wrote in message
news:MPG.15195e186b122c2b9897a0@news.supernews.com...
> And please don't overlook the big question regarding my entire original
> question.  What is correct standard-conformant behavior, and does anybody
> exhibit it?  Here's the current table of results:

Based on 14.8.3, 14.5.5.2 and 13.3.3 I believe that G++, and MWCW (and VC++
7.0 beta 1) are all correct in considering the last three cases ambiguous,
as you have a conflict in overload resolution between (G), (S1), and (S2)
My reading of 14.5.5.2 is that it is not applicable in comparing between G,
S1, S2 as you have overloaded function templates and they are not
specializations of each  other.  This leaves us with 14.8.3 and 13.3.3,
which would leave us with ambiguous calls as G resolves with S1 and S2
equally in cases where G's T2 argument deduces to char* or const char*.




---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrea Ferro" <AndreaF@UrkaDVD.it>
Date: Fri, 16 Mar 2001 21:34:45 GMT
Raw View
"Scott Meyers" <smeyers@aristeia.com> wrote in message
news:MPG.151bfd968637b2189897a4@news.supernews.com...
> On Fri, 16 Mar 2001 01:37:49 GMT, Jeff Peil wrote:
...
> > By changing S1 & S2 to be const references to const char* and char*
> > respectively you then reach the point where per item 4 S1&S2 are "at
> > least as specialized" as G while G is not "at least as specialized" as
> > S1&S2 which means that per item 5, S1&S2 are now specialized than G.
>
> This I don't follow.  Why must S1 and S2 pass their args by ref-to-const to
> be at least as specialized as G?

and in another post, "Scott Meyers" <smeyers@aristeia.com> wrote ...
> The terminology is ridiculous (para-, hyper-, meta-, and protonotions,
> anyone?), but it's also largely irrelevant to the basic question of whether
>
>   template<typename T1>                   // S1
>   void f(const T1&, const char*);
>
> is more specialized than
>
>   template<typename T1, typename T2>
>   void f(const T1&, const T2&);           // G

I'll try to elaborate it according to my understanding of the standard, however
note that I'm not totally sure I got 14.5.5.2 wording right.

The two templates are overloaded. There may be the case for one or the other to
be "more specialized" in the template function partial order sense. Let's start
by comparing G to S1.

G has two type template parameters. According to sub 3 we are to synthesize two
unique types. I'll call them __T1 and __T2. Then the transformed parameter list
is to be used for deduction for template S1. The deduction, in this case, fails
(the second parameter will never match). Therefore G is not as specialized as
S1.

Now I'll compare S1 to G. There's just one unique type __T1. The deduction for
the first parameter works, but the second parameter cannot be deduced. We are
trying to match "const char*" as an r-value. The closest thing we can have is
matching it to "const char*const&", but this is an l-value. And r-value to
l-value is not a conversion (if we were doing it in code, instead of being in an
algorithm, we would have a temporary variable created!). Therefore the two
templates are actually unrelated.

However note that if we change S1 to S1'

template<typename T1>                // S1'
void f(const T1&, const char*const&);

while comparing G to S1' still fails since "const __T2&" will not match to
"const char*const&" leading us to say G is not as specialized as S1', when we
compare S1' to G we do have indeed an exact match. Therefore S1' is at least as
specialized as G. For sub 5 we can say that S1' is more specialized than G.
Therefore when S1 and G are in scope, G is to be ignored when S1' is a viable
choice.

This, of course, if I get the standard right. Anyone to confirm (or confute)?

Andrea Ferro

---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info http://www.brainbench.com/transcript.jsp?pid=2522556



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrea Ferro" <AndreaF@UrkaDVD.it>
Date: Fri, 16 Mar 2001 18:13:21 GMT
Raw View
"Jeff Peil" <jpeil@bigfoot.com> wrote in message
news:3ab16dcb$1@news.microsoft.com...
>
> "Scott Meyers" <smeyers@aristeia.com> wrote in message
> news:MPG.151ab8dc7c832e2f9897a2@news.supernews.com...
> > On Thu, 15 Mar 2001 17:51:38 GMT, Anatoli wrote:
> > > My understanding is that "S1" is not a specialization
> > > of "G", but a different function template altogether.
> > > "G" requires two const references, and specializations
> > > of "G" can only vary types of these references.
> >
> > Ah.  Where in the standard is this specified, i.e., that if the general
> > template takes a const T&, all specializations must take a const U& where
> U is
> > more specialized than T?
>
> I think it's implicit in 14.5.5.2 item 4: "Using the transformed function
> parameter list, perform argument deduction against the other function
> tem-plate.
> The transformed template is at least as specialized as the other if, and
> only if, the deduction succeeds and the deduced parameter types are an exact
> match (so the deduction does not rely on implicit conver-sions)."
>
> If G is not a specialization of S1 and S1 is not a specialization of G, then
> I think all that can be said is that they are overloaded templates (and if
> both are in scope they will always resolve ambiguously.)  By changing S1 &
> S2 to be const references to const char* and char* respectively you then
> reach the point where per item 4 S1&S2 are "at least as specialized" as G
> while G is not "at least as specialized" as S1&S2 which means that per item
> 5, S1&S2 are now specialized than G.
>
> Perhaps this should be more explicit in the standard, and is worthy of a
> defect report?

I think the relevant wording is 14.7 sub 4. A specialization is either an
(implicit or explicit) instantiation or an esplicit specialization. Therefore a
definition of another template is not a specialization but an overloaded
template function. Some confusion may arise because of the wording in 14.5.5.2
(expecially subs 2, 4 and 5) where the concept of "more specialized" is used to
define partial ordering of *overloaded* template function specializations. For
example:

static unsigned logCount=0;

// this is a template
template <typename T>
void doLog( const T& )
{
    ++logCount;
}

// explicit instantiation of specialization for bool
template
void doLog<bool>(const bool& );

// explicit specialization for int
template <>
void doLog<int>( const int& )
{
    // do not log ints
}

....
    double d;
    bool b;
    int i;

    // call implicit instantiation of
    // specialization for double:
    // void doLog<double>(const double&)
    doLog(d);    // increments logCount

    // call explicit instantiation of
    // specialization for bool:
    // void doLog<bool>(const bool&)
    doLog(b);    // increments logCount

    // call explicit
    // specialization for int:
    // void doLog<int>(const int&)
    doLog(i);    // does not increment logCount
....

// this is another template that overloads
// the previous one
template <typename T>
void doLog( const T* const& )
{
    // do not log pointers
}

....
    char c;
    const char *p;

    // argument deduction for *second* template fails
    // call implicit instantiation of
    // specialization of *first* template for char:
    // void doLog<char>(const char&)
    doLog(c);    // increments logCount

    // argument deduction for both templates works giving:
    // void doLog<const char*>(const char*const&) // *first* template
    // void doLog<char>(const char*const&) // *second* template
    // and these would be ambiguous for overload resolution
    // but 14.5.5.2 sub 1 applies in this case and (AFAIK) the
    // *second* template is 'more specialized' in partial ordering
    // sense therefore the *first* template is to be ignored hence:
    // call implicit instantiation of
    // specialization of *second* template for char:
    // void doLog<char>(const char*const&)
    doLog(d);     // does not increment logCount
....

Note: in all this discussion the only part I'm not confortable with is the
wording in 14.5.5.2 sub 3. Specifically I'm not confortable with the concept of
"synthesized" unique types. The example in sub 5 is not helpfull. For example,
in this template what will the synthesis give as a "transformed function
parameter list" in case it is to be compared by argument deduction against
another (overloading) template:

template <typename T1, template <typename T2> class C, void (*P)(int), int I>
void foo( const T1*, C&, T&, C=P, T=I )
{
    // ...
}

assuming I haven't overlooked something and what I wrote does make sense :-(


Andrea Ferro

---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info http://www.brainbench.com/transcript.jsp?pid=2522556



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Fri, 16 Mar 2001 18:47:05 GMT
Raw View
On Fri, 16 Mar 2001 01:25:52 GMT, James Kuyper Jr. wrote:
> That's not the problem. The problem is that the standard doesn't allow
> for partial specialization of template functions. A function (templated
> or not) can overload the original, or it can be a fully specialized
> version of it, there's no other options.

>From a terminological point of view, there is another option:  one template
can be more specialized than another.  Witness paragraph 2 of 14.5.5.2:

  Given two overloaded function templates, whether one is more specialized
  than another can be determined by transforming each template in turn and
  using argument deduction (14.8.2) to compare it to the other.

Sure, there is no such thing as partial specialization of function
templates, but there is such a thing as the relative degree of
specialization of function templates.  And then we have implicit and
explicit function template specializations, both of which are different
again.

The terminology is ridiculous (para-, hyper-, meta-, and protonotions,
anyone?), but it's also largely irrelevant to the basic question of whether

  template<typename T1>                   // S1
  void f(const T1&, const char*);

is more specialized than

  template<typename T1, typename T2>
  void f(const T1&, const T2&);           // G

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Fri, 16 Mar 2001 19:11:58 GMT
Raw View
On Fri, 16 Mar 2001 01:37:49 GMT, Jeff Peil wrote:

> > Ah.  Where in the standard is this specified, i.e., that if the general
> > template takes a const T&, all specializations must take a const U&
> > where U is more specialized than T?
>
> I think it's implicit in 14.5.5.2 item 4: "Using the transformed function
> parameter list, perform argument deduction against the other function
> template.  The transformed template is at least as specialized as the
> other if, and only if, the deduction succeeds and the deduced parameter
> types are an exact match (so the deduction does not rely on implicit
> conversions)."

I still have no idea what this paragraph is supposed to mean, but the only
text I see that seems to support your position the the "exact match"
requirement.  But as table 9 in 13.3.3.1.1 makes clear, two types need not
be precisely the same to be an exact match.

> If G is not a specialization of S1 and S1 is not a specialization of G, then
> I think all that can be said is that they are overloaded templates (and if
> both are in scope they will always resolve ambiguously.)

I agree.

> By changing S1 & S2 to be const references to const char* and char*
> respectively you then reach the point where per item 4 S1&S2 are "at
> least as specialized" as G while G is not "at least as specialized" as
> S1&S2 which means that per item 5, S1&S2 are now specialized than G.

This I don't follow.  Why must S1 and S2 pass their args by ref-to-const to
be at least as specialized as G?

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrea Ferro" <AndreaF@UrkaDVD.it>
Date: Thu, 15 Mar 2001 23:19:10 GMT
Raw View
"Anatoli" <REMOVETHIS.anatoli@ptc.com> wrote in message
news:3AB0787E.AB304E61@ptc.com...
> Scott Meyers wrote:
> >   template<typename T1, typename T2>
> >   void f(const T1&, const T2&);           // G
> >
> >   template<typename T1>                   // S1
> >   void f(const T1&, const char*);
>
> My understanding is that "S1" is not a specialization
> of "G", but a different function template altogether.
> "G" requires two const references, and specializations
> of "G" can only vary types of these references.
>
>   template<typename T1>
>   void f(const T1&, const char* const&);  // S1'
>
> is a specialization of "G", at least according to g++.
> Another specialization is
>
>   template<typename T1>
>   void f(const T1&, char* const&);  // S2'
>
> These two give the desired result with g++. Sorry I
> don't have access to other modern compilers at
> the moment;

According to my understanding of the standard these are not specializations,
they still are just different overloaded template functions. However it is
likely that these do make the disambinguation according to the partial ordering
rule (however that's one rule I'm sure I understand completelly!).

One note to standard people: wouldn't it be much easier to allow for partial
specialization of template functions just the way it goes for classes already?

Andrea Ferro

---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info http://www.brainbench.com/transcript.jsp?pid=2522556



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrea Ferro" <AndreaF@UrkaDVD.it>
Date: Thu, 15 Mar 2001 21:07:33 GMT
Raw View
"Scott Meyers" <smeyers@aristeia.com> wrote in message
news:MPG.15186d2e7139b7a998979f@news.supernews.com...
> Given two types T1 and T2, I can write a template for performing some
> function f on objects of those types:
>
>   template<typename T1, typename T2>
>   void f(const T1&, const T2&);                // general template (G)
>
> When T2 is const char*, char*, or a string literal (const char[]), I have a
> more efficient way to perform f, so I'd like to overload f accordingly:
>
>   template<typename T1>                   // specialized template 1 (S1)
>   void f(const T1&, const char*);         // for T2 == const char* or
>                                           // T2 == a string literal
>
>   template<typename T1>                   // specialized template 2 (S2)
>   void f(const T1&, char*);               // for T2 == char*
>
> Given these templates, I expect the following calls to behave like this:
>
>   int x, y;
>   const char *pc;
>   char *p;
>                   // Expected
>                   // --------
>   f(x, y);        //     G
>   f(x, pc);       //     S1
>   f(x, "abc");    //     S1
>   f(x, p);        //     S2
>
> That's not what I get.  I submitted the above to VC 6.4, BCC 5.1.1, MWCW
> 6.1, g++ 2.95.2, and Como 4.2.44, and these are the results.  An entry of
> "A" means the compiler rejected the call as being ambiguous, and a "-"
> means there was no ambiguity, but I don't know which template was
> instantiated/called.  (My only access to Como is at the Comeau web site,
> and that offers only compiling, not execution.)
>
>                   //  VC   BCC  MWCW g++ Como
>                   //  --- ----- ---- --- ----
>   f(x, y);        //   G    G     G   G    -
>   f(x, pc);       //   S1   S1    A   A    -
>   f(x, "abc");    //   A    S2    A   A    A
>   f(x, p);        //   S2   S2    A   A    -
>
> The situation with the third call is tricky, because the type of "abc" is
> const char[4], not const char*.  I thought there was an outstanding issue
> about adding conversion from const char[n] to const char* during template
> argument deduction, but now I can't find it.  Anyway, since none of the
> above compilers agree with my expectation, there's clearly something wrong
> with my expectation.  But the compilers  also fail to agree with one
> another.  I'd like to know two things:
>   - What set of templates for f should, collectively, give me the behavior
>     I want?  That is, how can I specialize f's behavior when T2 is
>     a string literal or [const] char* pointer?
>   - Which, if any, compilers accept the "correct" solution?
> Thanks,


Just a foreword for terminology. In your comment you say that S1 and S2 are
"specialized" templates. In the standard a template function specialization is
an alternative function "implementation" for a given set of actual bindings of
the template parameters. Therefore S1 and S2 are not, technically,
specializations. They are new templates that happen to overload G.

That said I think MWCW and g++ are correct.

This is my reasoning:

--- First case
f(x, y);

S1 and S2 do not apply because there's no way to convert the second argument. A
G specialization is called with T1=int and T2=int.

--- Second case
f(x, pc);
S2 does not apply because you cannot convert the second argument. However this
is ambiguous because both G and S1 can be specialized to satisfy the arguments
and neither call would require a conversion. Specifically S1 can be specialized
with T1=int and G can be specialized with T1=int and T2=const char*. That done
the first parameter is the same and the two resulting functions differ only in
the second argument. However these are a pointer to a constant char and a
constant reference to a pointer to a constant char. The difference is in passing
it by reference or by value. That's ambiguous.

To see it better you may consider the simpler case of "int" instead of "const
char*". Assume you had:

template<typename T1, typename T2>
void foo(const T1&, const T2&);

template<typename T1>
void foo(const T1&, int);

int i,j;

foo(i,j);

the call to foo is ambiguous. Both templates can be specialized giving

void foo<int,int>( const int&, const int&);
void   foo<int>  ( const int&,    int    );

See the ambiguity now?


--- Third case
f(x, "abc");

This is not much different than case 2 beside the fact that G should consider it
an array of constant chars. There's still a resolution ambiguity between G and
S1. I'm surprised that BCC specialized S2 because that's obviously wrong for
2.13.4 sub 1 (a string literal has type array of constant chars) and no
conversion rule can silently cast away const.


--- Fourth case
f(x, p);

This is trice ambiguous. Same reasoning we had for second case plus S2 can also
apply!



Andrea Ferro

---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info http://www.brainbench.com/transcript.jsp?pid=2522556



---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Thu, 15 Mar 2001 07:32:05 GMT
Raw View
On Thu, 15 Mar 2001 01:02:13 GMT, Jeff Peil wrote:
> Based on 14.8.3, 14.5.5.2 and 13.3.3 I believe that G++, and MWCW (and VC++
> 7.0 beta 1) are all correct in considering the last three cases ambiguous,
> as you have a conflict in overload resolution between (G), (S1), and (S2)
> My reading of 14.5.5.2 is that it is not applicable in comparing between G,
> S1, S2 as you have overloaded function templates and they are not
> specializations of each  other.

I agree that 14.5.5.2 is the critical part of the standard for this
question.  I'd like to think I have an intuitive notion of what it means
for one template to be more specialized than another, but I can't make
heads or tails of the formal description in paragraphs 3-5.

Let's consider only G and S1:

  template<typename T1, typename T2>
  void f(const T1&, const T2&);           // G

  template<typename T1>                   // S1
  void f(const T1&, const char*);

Intuitively, S1 is more specialized than G, because G can be called with
any type as its second parameter but S1 insists on either const char* or
something convertible to it.

Does the standard not say that this is the case?

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Thu, 15 Mar 2001 23:19:34 GMT
Raw View
On Thu, 15 Mar 2001 17:51:38 GMT, Anatoli wrote:
> My understanding is that "S1" is not a specialization
> of "G", but a different function template altogether.
> "G" requires two const references, and specializations
> of "G" can only vary types of these references.

Ah.  Where in the standard is this specified, i.e., that if the general
template takes a const T&, all specializations must take a const U& where U is
more specialized than T?

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]





Author: Scott Meyers <smeyers@aristeia.com>
Date: Wed, 14 Mar 2001 01:47:33 GMT
Raw View
Given two types T1 and T2, I can write a template for performing some
function f on objects of those types:

  template<typename T1, typename T2>
  void f(const T1&, const T2&);                // general template (G)

When T2 is const char*, char*, or a string literal (const char[]), I have a
more efficient way to perform f, so I'd like to overload f accordingly:

  template<typename T1>                   // specialized template 1 (S1)
  void f(const T1&, const char*);         // for T2 == const char* or
                                          // T2 == a string literal

  template<typename T1>                   // specialized template 2 (S2)
  void f(const T1&, char*);               // for T2 == char*

Given these templates, I expect the following calls to behave like this:

  int x, y;
  const char *pc;
  char *p;
                  // Expected
                  // --------
  f(x, y);        //     G
  f(x, pc);       //     S1
  f(x, "abc");    //     S1
  f(x, p);        //     S2

That's not what I get.  I submitted the above to VC 6.4, BCC 5.1.1, MWCW
6.1, g++ 2.95.2, and Como 4.2.44, and these are the results.  An entry of
"A" means the compiler rejected the call as being ambiguous, and a "-"
means there was no ambiguity, but I don't know which template was
instantiated/called.  (My only access to Como is at the Comeau web site,
and that offers only compiling, not execution.)

                  //  VC   BCC  MWCW g++ Como
                  //  --- ----- ---- --- ----
  f(x, y);        //   G    G     G   G    -
  f(x, pc);       //   S1   S1    A   A    -
  f(x, "abc");    //   A    S2    A   A    A
  f(x, p);        //   S2   S2    A   A    -

The situation with the third call is tricky, because the type of "abc" is
const char[4], not const char*.  I thought there was an outstanding issue
about adding conversion from const char[n] to const char* during template
argument deduction, but now I can't find it.  Anyway, since none of the
above compilers agree with my expectation, there's clearly something wrong
with my expectation.  But the compilers  also fail to agree with one
another.  I'd like to know two things:
  - What set of templates for f should, collectively, give me the behavior
    I want?  That is, how can I specialize f's behavior when T2 is
    a string literal or [const] char* pointer?
  - Which, if any, compilers accept the "correct" solution?
Thanks,

Scott

---
[ 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://www.research.att.com/~austern/csc/faq.html                ]