Why is type deduction on const char[] different to const char *?

Viewed 208

In the first call, when I pass a char const [] into a template function with a parameter of T const a, T is deduced as char const * which is reasonable because const refers to the decaying pointer.

However, when the parameter type is changed to T const & a, T is deduced as char[7]. From the point of view above, why doesn't the const qualify the whole array type?

template <typename T>
void show1(T const a) {
     // input is const char *
     // T is char const *
     // a is char const * const
}

template <typename T>
void show2(T const & a) {
     // input is char const [7]
     // T is char[7]
     // a is char const (&)[7]
}

int main() {
    const char s[] = "asdasd";
    show1(s);
    show2(s);
}
3 Answers

why doesn't the const qualify the whole array type

Because for array type,

(emphasis mine)

Applying cv-qualifiers to an array type (through typedef or template type manipulation) applies the qualifiers to the element type, but any array type whose elements are of cv-qualified type is considered to have the same cv-qualification.

// a and b have the same const-qualified type "array of 5 const char"
typedef const char CC;
CC a[5] = {}; 
typedef char CA[5];
const CA b = {};

That means when T is char[7] T const leads to the type char const[7], then T const& (i.e. a's type) is char const (&)[7].

On the other hand, when you pass the array s with type const char[7], the array is considered as const-qualified too. So given the parameter type T const&, T is deduced as char[7] (but not char const[7]).

This is because arrays are non-copyable and non-assignable in C++.

So in calling show1, the const char[] type decays to a const char*. The language permits one implicit conversion per parameter at a function call site.

With show2, you are passing by reference - no copy or assignment is required, so pointer decay does not occur.

template <typename T>
void show(ParameterType a) {
}

show(expression);

Compiler uses expression to deduce T and ParameterType. T and ParameterType are different if ParameterType contains qualifiers like const.

If ParameterType is neither pointer nor reference (case of your show1, T const), type of T is type of expression without const, volatile and reference. So T is type of const char *. ParameterType (type of a) is const char * const.

If ParameterType (T const & in your show2) is pointer or reference (but not reference like T&&). First ignore reference, which gives results T const (same as const T). Second match type of expression (const char []) to const T, so T is char [].

For more information, Item 1 of Effective Modern C++ by Scott Meyers is exactly what you want. The rule is more complicated then which I described here but very important.

Related