tags:

views:

224

answers:

5

Given the code below, why is the foo(T*) function selected ?

If I remove it (the foo(T*)) the code still compiles and works correctly, but G++ v4.4.0 (and probably other compilers as well) will generate two foo() functions: one for char[4] and one for char[7].

#include <iostream>
using namespace std;

template< typename T >
void foo( const T& )
{
    cout << "foo(const T&)" << endl;
}

template< typename T >
void foo( T* )
{
    cout << "foo(T*)" << endl;
}

int main()
{
    foo( "bar" );
    foo( "foobar" );
    return 0;
}
A: 

Cause " " is a char*, which fits perfectly to foo(T*) function. When you remove this, the compiler will try to make it work with foo(T&), which requires you to pass reference to char array that contains the string.

Compiler can't generate one function that would receive reference to char, as you are passing whole array, so it has to dereference it.

Ravadre
Wrong too. Please don't guess. `""` is a `char const[1]`. You also cannot `pass reference`. Expressions always have non-reference type.
Johannes Schaub - litb
Ravadre
A: 

That's because the type of "bar" is indeed char[4], but that easily decays into a char*.

If you want to "catch" instantiations with string literals you would need to add the following overload (note: there's no partial specialization for function templates, there's only overloading):

template< typename T, std::size_t S >
void foo(T(&)[S])
{
  std::cout << "foo(T(&)[S])";
}

This would still cause the compiler to create different instances for "foo" and "foobar" (with different S), but you could easily make this an inline function which forwards to some common implementation.

sbi
Well, then it's easier to provide the T* version of foo()
CsTamas
CsTamas
@CsTamas: I'm not sure what you're asking. The above fits an array, but won't catch a pointer. If he doesn't want the `T*` version to catch both (which is my interpretation of his -- admittedly vague -- question), this would prevent it.
sbi
@sbi: not talking about catching the string literals. So what is the exact type of string literals ? char[n] or char* ?
CsTamas
Rob Kennedy
@Nick, you can write foo(const T(
AProgrammer
The exact type of string literals is "array of const-char" (subclause 2.13.4 of the C++ standard), which for the purposes of template arguments is const char [4] (for "bar"), but since he didn't write a specialization for const char [4], const T* is preferred.
Nick Bastin
@AProgrammer: Sure, my point was that if you're going to try to capture an arbitrary-sized array (e.g. not write an overload for every size), const char * is your type of choice.
Nick Bastin
`void foo(T( template<typename T> void f(T*); int main() { f(""); }` is ambiguous. I think you agree, partial ordering is complicated :)
Johannes Schaub - litb
@Rob Kennedy: I added that overload to show that there's even a better match than the `char*` one, but I guess this isn't very clearly expressed. :(
sbi
@litb: Indeed, I should have asked the compiler before hitting submit. :( Is there no way to distinguish a pointer from an array?
sbi
Johannes Schaub - litb
@litb: Ah, that's a solution. I usually never even bother with overloading function templates, but just forward to partially specialized class templates. However, I thought this was to complicated here, so I tried a function-template-only solution. That must be why I blew it so badly. :)
sbi
+4  A: 
Johannes Schaub - litb
Nick Bastin
sorry i overlooked the template<...> clause before the second one. fixed :)
Johannes Schaub - litb
Your assertion that the match to `T*` is closer is wrong, though :)
Johannes Schaub - litb
Nick Bastin
@litb: I think we just disagree on the definition of "closer"...I merely mean "closer" to be "more preferable" in the eyes of the standard (and thus, compiler).
Nick Bastin
Richard Corden
@Richard, sure your previous comment was fine :) (maybe i'm gonna enhance my answer, but not now i'm waaay too tired right now). :)
Johannes Schaub - litb
A: 

Based on overload resolution rules (Appendix B of C++ Templates: The Complete Guide has a good overview), string literals (const char []) are closer to T* than T&, because the compiler makes no distinction between char[] and char*, so T* is the closest match (const T* would be an exact match).

In fact, if you could add:

template<typename T>
void foo(const T[] a)

(which you can't), your compiler would tell you that this function is a redefinition of:

template<typename T>
void foo(const T* a)
Nick Bastin
`T*` is by no means a closer match. Try `void f(char const( void f(char const*); int main() { f(""); }`: This call, which resembles the situation of above using non-templates, is ambiguous.
Johannes Schaub - litb
Nick Bastin
AProgrammer
@AProgrammer: Right, I didn't mean to suggest that you can't do that (in fact, it's clear that the compiler will, given no other options), but merely that it is less preferred.
Nick Bastin
@Nick, Please quote the appropriate Standard sections, instead of making more and more claims. Your analysis seems to be slightly off to me, frankly speaking.
Johannes Schaub - litb
Nick Bastin
@Nick, "123" binding to a `T const`. But in our examples, there nowhere appears a type `char*`.
Johannes Schaub - litb
Note that there is a "reference compatible with added qualification". This happens for example with `void f(int const int a; int main() { f(a) }`. But this will still be an exact match (identity conversion). Nevertheless, it can influence the rank of the conversion sequence, as shown by `void f(int const void f(int int a; int main() { f(a); }` it will call the second one, because it is less const qualified. But it won't do any conversion. When doing argument deduction against `T*`, (argument is still `"123"` with type `char const[4]`), an lvalue transformation ...
Johannes Schaub - litb
(array to pointer conversion) happens by the deduction process, to get type `char const*` (then `A = char const*` and `P = T*` when entering `14.8.2.4`). The compiler will then deduce `char const` for T, and thus will build a function parameter of type `char const*`. Now, `"123"` needs a lvalue transformation for the call to succeed (array to pointer), but no qualification conversion or whatsoever. So, actually, the `T*` one has one conversion more. This array->pointer conversion, however, is ignored when comparing two conversion sequences. And this, the call would be ambiguous ...
Johannes Schaub - litb
Now, the compiler sees that both candidates are functions that were instantiated from templates, and will lookup their templates, determining a partial ordering. Then, the candidate corresponding to the more specialized template (if there exist an order between them) will be chosen by overload resolution. And that is in our case the `T*` version. Hope my analysis in the comments clear the matter up.
Johannes Schaub - litb
+2  A: 

The full answer is quite technical.

First, string literals have char const[N] type.

Then there is an implicit conversion from char const[N] to char const*.

So both your template function match, one using reference binding, one using the implicit conversion. When they are alone, both your template functions are able to handle the calls, but when they are both present, we have to explain why the second foo (instantiated with T=char const[N]) is a better match than the first (instantiated with T=char). If you look at the overloading rules (as given by litb), the choice between

void foo(char const (&x)[4));

and

void foo(char const* x);

is ambigous (the rules are quite complicated but you can check by writing non template functions with such signatures and see that the compiler complains). In that case, the choice is made to the second one because that one is more specialized (again the rules for this partial ordering are complicated, but in this case it is because you can pass a char const[N] to a char const* but not a char const* to a char const[N] in the same way as void bar(char const*) is more specialized than void bar(char*) because you can pass a char* to a char const* but not vise-versa).

AProgrammer