模板推导:const引用和const指针

template deduction: const reference and const pointer

template <typename T>
void f(T t)
{}

int x = 1;
const int & rx = x;
const int * px = &x;
f(rx); // t is int
f(px); // t is const int *, instead of int *, WHY???

我现在很困惑。根据 Effective Modern c++,

It’s important to recognize that const is ignored only for by-value parameters. As we’ve seen, for parameters that are references-to- or pointers-to-const, the constness of expr is preserved during type deduction.

我觉得是

template <typename T>
void f(T * t)
{}
f(px); // t is const int *

template <typename T>
void f(T & t)
{}
f(cx); // t is const int &

template <typename T>
void f(T t)
{}
f(value); // const or volatile of value will be ignored when the type of the parameter t is deduced

所以我觉得上面f(px)时,t应该是int *,但实际上是const int *.

为什么reference的const被忽略了,pointer的const却没有? 或者,为什么 rx const int &?

仅忽略顶级 const/volatile 限定符。所有其他都是你类型的内在品质。换句话说,您正在复制一个指针——这意味着该函数对一个副本进行操作,并且对其进行的任何修改(如分配另一个变量的地址)都不会修改原始指针。但是如果你传递一个指向 const int 的指针,让函数修改整数是非常违反直觉的。

template <typename T>
void f(T t)
{
    t = &another_variable; // ok
}

f(px);

void f(T t)
{
    *t = 42; // not ok!
}

f(px); // secretly modifying `x` through a pointer to const...

参考:this answer 指向 const 的指针与 const 指针的区别

So I think when f(px) above, px should be int *, but in fact it is const int *.

重点是参数的类型,按值传递passed-by-reference/pointer的行为发生变化。

按值传递时,忽略参数本身的常量性。对于指针参数,指针的常量性被忽略(const pointer -> pointer),但pointee的常量性仍然保留(pointer to const -> pointer to const)。

这是有道理的,因为当传递一个指针时,指针本身被复制,但是指针对象是相同的,它们都指向同一个东西,所以指针对象的常量性被保留;从调用者的角度来看,他们不希望修改对象,他们可能会在以后使用它。而传递一个引用(这里引用其实并不重要),你会得到一个全新的值,与原来的值无关,然后constness被忽略。

正如书上的以下解释,当const指针指向const(aconst char* const)传递时,

template<typename T>
void f(T param); // param is still passed by value

const char* const ptr = // ptr is const pointer to const object
  "Fun with pointers";

f(ptr); // pass arg of type const char * const

param 推导的类型将是 const char*