It’s generally a bad idea to overload a function template taking a “T&&” parameter, because that can bind to anything, but let’s suppose we do it anyway:
template<typename T>
void func(const T& param)
{
std::cout << "const T&\n";
}
template<typename T>
void func(T&& param)
{
std::cout << "T&&\n";
}
My understanding has been that the const T& overload will get called for arguments that are const lvalues, and the T&& overload will get called for all other argument types. But consider what happens when we call func with arrays of const and non-const contents:
int main()
{
int array[5] = {};
const int constArray[5] = {};
func(array); // calls T&& overload
func(constArray); // calls const T& overload
}
VC10, VC11, and gcc 4.7 agree on the results shown. My question is why the second call invokes the const T& overload. The simple answer is that constArray has a const in it, but I think that’s too simple. The type T that is deduced (regardless of the template selected) is “array of 5 const ints” so the type of param in the const T& overload would be “reference to const array of 5 const ints”. But the array named constArray is not itself declared const. So why doesn’t the call to func(constArray) invoke the T&& overload, thus yielding a type for param of “reference to array of 5 const ints”?
This question is motivated by the discussion associated with the question at c++ template function argument deduce and function resolution, but I think that thread got sidetracked on other issues and did not clarify the question I’m now asking here.
In function parameter lists (as well as everywhere else), a cv qualification on an array type is shuffled right to qualify the array element type. For example, with
T = int [5],const T &is converted toint const (&) [5].So the call to
funcwith an argument of typeint const [5]is deduced as a call to either of:Both overloads are viable, but the former is preferred:
Let T1 be the
const T &template and T2 be theT &&template; that is, their parameter types are T1 :=const T &and T2 :=T &&. Then the transformed argument types (14.5.6.2:3) can be written A1 :=const C &, A2 :=D &&for synthesized typesC,D.Now, we attempt to order T1 against T2 (14.8.2.4:2), first using A1 as the argument template and P2 as the parameter template. We remove references (14.8.2.4:5) giving A1 ->
const Cand T2 ->T, then remove cv-qualification (14.8.2.4:7) giving A1 ->Cand T2 ->T. The templateTcan be deduced toC(14.8.2.4:8) so A1 is at least as specialised as P2; conversely, A2 ->D->D, P1 ->const T->T, andTcan be deduced toD, so A2 is at least as specialised as P1.This would usually imply that neither is more specialised than the other; however, because the
PandAtypes are reference types 14.8.2.4:9 applies, and since A1 is an lvalue reference and P2 is not, T1 is considered more specialized than T2. (A tie between reference types can also be broken by cv-qualification under the same clause.)