Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function argument binding rules for passing an array by reference vs passing pointer

To prevent any confusion, I very much understand the difference between arrays and pointers, the concept of decay-to-pointer, and the concept of passing an array by reference in C++, etc.

My question here is specifically about the rules used by the compiler to select a function from a set of function overload candidates, when one overload takes an array reference, and the other overload takes a pointer.

For example, suppose we have:

template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
    std::cout << "Array-reference overload!" << std::endl;
}

template <class T>
void foo(const T* ptr)
{
    std::cout << "Pointer overload!" << std::endl;
}

If we attempt to invoke function template foo() as follows:

const char arr[2] = "A";
foo(arr);

... then my expectation would be that the first overload, the one that takes an array reference, would be selected by the compiler.

However, using GCC 4.9.2, if I compile the above code, I get an error:

test.cpp:28:9: error: call of overloaded ‘foo(const char [2])’ is ambiguous

It's unclear to me why both overloads are considered equally good candidates by the compiler here, since the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.

Now, I am able to get the above overload working by explicitly using type_traits as follows:

template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
    std::cout << "Array-reference overload!" << std::endl;
}

template <class T>
void foo(T ptr, typename std::enable_if<std::is_pointer<T>::value>::type* = 0)
{
    std::cout << "Pointer overload!" << std::endl;
}

In this case, the program compiles and the overload that takes an array reference is selected. However, I don't understand why this solution should be necessary. I'd like to understand why the compiler considers a function that requires decay-to-pointer an equally likely overload candidate as the array reference, when the argument passed is very much an array.

like image 220
Siler Avatar asked Jun 29 '17 02:06

Siler


People also ask

Can you pass an array by reference?

Arrays can be passed by reference OR by degrading to a pointer. For example, using char arr[1]; foo(char arr[]). , arr degrades to a pointer; while using char arr[1]; foo(char (&arr)[1]) , arr is passed as a reference. It's notable that the former form is often regarded as ill-formed since the dimension is lost.

Are pointers passed by reference or value?

Pointers are passed by value as anything else. That means the contents of the pointer variable (the address of the object pointed to) is copied. That means that if you change the value of the pointer in the function body, that change will not be reflected in the external pointer that will still point to the old object.

Do you need to pass an array by reference in C++?

A whole array cannot be passed as an argument to a function in C++. You can, however, pass a pointer to an array without an index by specifying the array's name.

How many different ways are possible to pass a pointer to a function call?

There are three ways to pass variables to a function – pass by value, pass by pointer and pass by reference.


1 Answers

the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.

Because when checking the ranking of implicit conversion sequences in overload resolution, the array-to-pointer conversion is considered as an exact match, thus the 2nd overload has the same rank with the 1st one.

From the standard, $16.3.3.1.1 Standard conversion sequences [over.ics.scs] Table 13 — Conversions

Conversion                   Category               Rank         Subclause
No conversions required      Identity               Exact Match
... ...
Array-to-pointer conversion  Lvalue Transformation  Exact Match  [conv.array]
... ...

It's worth noting that the rank of "No conversions required" (i.e. the case for the 1st overload) is "Exact Match" too.

like image 52
songyuanyao Avatar answered Sep 27 '22 02:09

songyuanyao