Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the first function call bind to the first function? [duplicate]

Why does the first function call (cm(car);) bind to the first function?

I understand that second call is bound to second function because it's non-template, despite both being perfect matches.

If the first function is defined as non-template with fixed array length, as:

    void cm(const char (&h)[8]) {cout << "const char (&)[8]" << endl;}

than again it gets selected over the second one (the second call will be ambiguous that way).

Code:

template<size_t N> void cm(const char (&h)[N]) 
    {std::cout << " const (&)[N] " << endl;}

void cm(const char * h)
    {cout << " const char * " << endl;}

int main()
{
    char car[] = "errqweq";
    const char ccar[] = "errqweq";
    cm(car);
    cm(ccar);
}

Output:

 const (&)[N]
 const char * 
like image 670
MKPS Avatar asked Nov 03 '14 22:11

MKPS


Video Answer


2 Answers

The first call chooses the function template specialization - because it's a better match.
Let us label both overloads:

template<size_t N> void cm(const char (&h)[N])  // (1) - the specialization
    {std::cout << " const (&)[N] " << endl;}

void cm(const char * h)                         // (2)
    {cout << " const char * " << endl;}

For (1), car binds to a reference. That is an identity conversion1. For (2), after the array-to-pointer conversion of car, which yields char*2, a qualification conversion has to be done so char* becomes
char const*. That is now invoking this:

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,
  • […]

An array-to-pointer conversion is an Lvalue Transformation, so it isn't considered here - just as in the second example. The qualification conversion has an own category though: Qualification Adjustment. Therefore the conversion to the parameter of (1) is a subsequence of the conversion to the parameter of (2): The first is an identity conversion and the second a qualification conversion, and according to the paragraph above an identity conversion is a subsequence of any non-identity conversion. So (1) is chosen.

As you already mentioned yourself, in the second case the conversions are equally good; The quote above does not work since the conversion to (2)s parameter is not a subsequence of the conversion to the parameter of (1). Hence, [over.match.best]/1 applies.

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

  • for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
  • the context is an initialization by user-defined conversion […], or, if not that,
  • F1 is a non-template function and F2 is a function template specialization,

So (2) one is chosen. If the function template wasn't a template but a function with parameter
char const (&)[8] the call would be ambiguous as Clang correctly says.


1 [over.ics.ref]/1:

When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1).

[dcl.init.ref]/5 (which is in 8.5.3):

In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.


2 [conv.array]:

An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.

T can be cv-qualified, and so would the type of the pointee be. Here T is just char, so the pointer is of type pointer to char => char*.

like image 161
Columbo Avatar answered Oct 22 '22 18:10

Columbo


Because the string "errqweq" directly written in the code is read only because it is, at run time, in a part of memory "protected" as it is managed as a constant.

Pointing at it using a const char* ccar; or a const char ccar[]; is correct. You are pointing to the memory holding the original "errqweq" with the const specifier: the compiler ensure that the string will be not modified.

But look at: char car[] = "errqweq";

In order to provide you a modificable buffer (as you are requesting without the const modifier) the compiler create an array of 8 elements (7 chars + \0) on the stack copying in it (i.e: intializing it) the string "errqweq".

So the first call is using a char buffer[8] arguments that is safely converted to a const char buffer[8]. Obviously the fixed size of the array give a best match with the template instead of the more "weak" bind with the function that requires "just" a constant pointer.

like image 27
Stefano Buora Avatar answered Oct 22 '22 19:10

Stefano Buora