Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the rules for choosing from overloaded template functions?

Tags:

c++

templates

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;
}
like image 633
CsTamas Avatar asked Jul 24 '09 13:07

CsTamas


2 Answers

Formally, when comparing conversion sequences, lvalue transformations are ignored. Conversions are grouped into several categories, like qualification adjustment (T* -> T const*), lvalue transformation (int[N] -> int*, void() -> void(*)()), and others.

The only difference between your two candidates is an lvalue transformation. String literals are arrays that convert to pointers. The first candidate accepts the array by reference, and thus won't need an lvalue transformation. The second candidate requires an lvalue transformation.

So, if there are two candidates that both function template specializations are equally viable by looking only at the conversions, then the rule is that the more specialized one is chosen by doing partial ordering of the two.

Let's compare the two by looking at their signature of their function parameter list

void(T const&);
void(T*);

If we choose some unique type Q for the first parameter list and try to match against the second parameter list, we are matching Q against T*. This will fail, since Q is not a pointer. Thus, the second is at least as specialized as the first.

If we do the other way around, we match Q* against T const&. The reference is dropped and toplevel qualifiers are ignored, and the remaining T becomes Q*. This is an exact match for the purpose of partial ordering, and thus deduction of the transformed parameter list of the second against the first candidate succeeds. Since the other direction (against the second) didn't succeed, the second candidate is more specialized than the first - and in consequence, overload resolution will prefer the second, if there would otherwise be an ambiguity.

At 13.3.3.2/3:

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...]

  • S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that [...]

Then 13.3.3/1

  • let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.

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 [...]

  • F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2, or, if not that, [...]

Finally, here is the table of implicit conversions that may participate in an standard conversion sequence at 13.3.3.1.1/3.

Conversion sequences http://img259.imageshack.us/img259/851/convs.png

like image 113
Johannes Schaub - litb Avatar answered Sep 24 '22 23:09

Johannes Schaub - litb


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).

like image 22
AProgrammer Avatar answered Sep 26 '22 23:09

AProgrammer