Home > database >  Array reference binding vs. array-to-pointer conversion with templates
Array reference binding vs. array-to-pointer conversion with templates

Time:04-02

This code sample fails to compile due to ambiguous overload resolution

void g(char (&t)[4]) {}
void g(char *t) {}

int main()
{
  char a[] = "123";
  g(a);
}

and careful reading of overload resolution rules makes it clear why it fails. No problems here.

If we formally transform it into a template version

template <typename T> void g(T (&t)[4]) {}
template <typename T> void g(T *t) {}

int main()
{
  char a[] = "123";
  g(a);
}

it will continue to behave "as expected" and fail with an ambiguity of the same nature. So far so good.

However, the version below compiles without any issues and choses the second overload

template <typename T> void g(T &t) {}
template <typename T> void g(T *t) {}

int main()
{
  char a[] = "123";
  g(a);
}

If we comment out the second overload, the first one will be used successfully with T deduced as char [4], i.e. template argument deduction works as expected for the first version, effectively making it equivalent to void g(char (&t)[4]). So, on the first naive sight this third example should behave the same as the previous two.

Yet, it compiles. What [template] overload resolution rule kicks in in this third case to save the day and direct the compiler to choose the second overload? Why does it prefer array-to-pointer conversion over direct reference binding?

P.S. I find quite a few questions on SO that target very similar issues, but they all seem to differ in some important nuances.

CodePudding user response:

The second overload is more specialized than the first one during partial ordering of function templates.

According to [temp.deduct.partial]/5 the reference on T &t of the first overload is ignored during template argument deduction performed for partial ordering. The following paragraphs distinguish based on reference/value category only if both parameters are reference types.

Then T of the first overload can always deduce against a type A* invented from the parameter of the second overload, but T* of the second overload can't deduce against a type A invented from the parameter of the first overload.

Therefore the second overload is more specialized and is chosen.


With T (&t)[4] argument deduction in both directions will fail because deduction of T[4] against A* will fail and so will deduction of T* against A[4]. Array-to-pointer decay of the array type is specified for template argument deduction for a function call but not for template argument deduction for partial ordering. See also active CWG issue 402.

So neither template will be more specialized in this case and the partial ordering tiebreaker does not apply.


The array-to-pointer conversion is not relevant. It is not considered any worse than the identity conversion sequence (see [over.ics.rank]/3.2.1 excluding lvalue transformations which array-to-pointer conversions are).

  • Related