问题
Let's consider those definitions:
/*** full type information with typeid ***/
template <class> class Type{};
template <class T> std::string typeStr()
{ return typeid(Type<T>).name(); }
/*** function template for parameter deduction ***/
template <class T> void func(const T &a)
{
std::cout << "Deduced type for T is: " << typeStr<T>() << std::endl;
std::cout << "\targument type is: " << typeStr<decltype(a)>() << std::endl;
}
with pointers to const
If the following statements are executed:
const int i=5, *ip=&i;
func(ip);
The output is:
Deduced type for T is: 4TypeI**PKi**E
So T
is actually deduced as a pointer to a constant integer. The fact that the argument is a reference-to-const does not change the deduction, which is what one would expect because the constness of the pointer is low-level.
but with array of const
Nonetheless, if following statements are executed:
const int ia[3] = {3, 2, 1};
func(ia);
The output is:
Deduced type for T is: 4TypeI**A3_i**E
So T
is actually deduced as an array of 3 non-const integers. The fact that the argument is a reference-to-const does change the deduction, as if the const
was slipping into the array elements.
Actually, versions of CL up to 18 were deducing T
as array of 3 const integers was what I expected to be standard, but it seems that since v19 it converged to what GCC and Clang are doing (i.e., deducing as non-const).
Thus, I assume the later behaviour to be standard, but was is the rationale ? It could seem surprising that it does not behave like with pointers.
Edit: Following dip comment, I will report here pointers to CWG issues related to this behaviour, pointers he actually posted as a comment on this answer (answer that actually raised this new question... C++ feels like a deep tunnel)
- CWG 1059
- CWG 1610
- CWG 112
回答1:
Using this function template prototype:
template <typename T> void func(const T& a);
In your first example, the type deduction works as:
const int* ip;
func(ip) => func<const int*>(const (const int*)& a)
^^^^^^^^^^ ^^^^^^^^^^
Note: This is pseudocode. The full type is const int* const&
.
Note that the const int
remains const int
, but the *
becomes * const
.
This is because const int*
is just a regular, mutable, non-volatile pointer. It is just a *
. What it points to is irrelevant.
But in the second example, you have:
const int ia[3];
func(ia) => func<int[3]>(const (int[3])& a)
^^^^^^ ^^^^^^
Note: This is pseudocode. The real type would be const int (&a)[3]
.
So the type deduction is working the same in both cases, discarding the outer const
.
It so happens that a const
array is the same as an array of const
elements.
It might help to write types like this:
template <typename T> func(T const & a);
int const * ip;
func(ip) => func<int const *>(int const * const & a)
int const ia [3];
func(ia) => func<int [3]>(int const (& a) [3])
On that second example, the const
appears to "move" from being applied on the array to being applied on the elements. This is because you can't really have a const
array, only an array of const
elements.
来源:https://stackoverflow.com/questions/30893036/why-does-template-parameter-deduction-for-t-skips-the-constness-of-array-eleme