7
#include <iostream>
using namespace std;

void f(const char* arg)
{
    cout << "arg is a pointer" << endl;
}

template<size_t N>
void f(const char (&arg)[N])
{
    cout << "arg is an array." << endl;
}

int main()
{
    f("");
}

My compiler is clang 3.8.

The output is:

arg is a pointer

However, according to cppreference.com,

The type of an unprefixed string literal is const char[].

Why does the overload resolution not behave as expected?

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • 2
    Equivalent example, but abstracting away the template: http://melpon.org/wandbox/permlink/0nGenu5Ysj40wS8u โ€“ Baum mit Augen Jan 03 '17 at 03:26
  • [Closely related](https://stackoverflow.com/questions/16708307/is-it-possible-to-legally-overload-a-string-literal-and-const-char), possible dupe. What do you think? โ€“ Baum mit Augen Jan 03 '17 at 03:30

2 Answers2

8

It does behave as expected, you just need to adjust your expectations ;-)

const char[1] and const char (&)[1] are different types.

The conversions to const char* (array-to-pointer conversion) and const (&char)[1] (identity conversion) are both considered exact matches, but a non-template is a better match than a template.

If you write a non-template size-specific overload,

void f(const char (&arg)[1])

you will get an error that the function call is ambiguous.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
1

@molbdnilo's answer is correct. To add one detail: Your intuition would be correct and the compiler would prefer to avoid the array-to-pointer conversion by calling the template. But lvalue transformations (lvalue-to-rvalue, array-to-pointer, and function-to-pointer) are specifically ignored in overload ranking, according to [over.ics.rank] ยง13.3.3.2/3.2.1.

There is a workaround: add a fake volatile to restore the balance of overload preference. Just be sure to remove it by const_cast before using the parameter.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421