为什么对引用的 const 引用会失去其常量性?

Why does a const reference to a reference lose its constness?

鉴于此代码:

#include <iostream>

template<typename T>
void modify(const T &j){ j = 42; } // j has type int&

int main()
{
    int i = 10;
    modify<int&>(i); // T=int&
    std::cout << i;  // 42 is printed
}

如果T=int&为什么const T &j变成int &jconst 会怎样?

不存在对引用的引用,即不存在 T & &

给定 const T&,其中 Tint&,类型折叠为 int&

What happens to const?

也没有 const 引用这样的东西,即没有 T & const(不要与 const 的引用相混淆,const 确实存在并且通常通俗地称为 const 引用)。不能修改任何引用(这与修改引用的对象不同),因此常量性没有意义。 const 在这里被简单地忽略了。

标准规则(来自最新草案):

[dcl.ref] If a typedef-name ([dcl.typedef], [temp.param]) or a decltype-specifier ([dcl.type.simple]) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR. [ Note: This rule is known as reference collapsing. — end note ]

这里的 cv 指的是 cv 限定符,即 const 和 volatile。

阐明为什么这适用于模板参数:

[temp.param] A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name ...


P.S。指定引用折叠的方式是完美转发工作的部分原因。

鉴于 const T&constT 本身上是合格的。当Tint&时,const T表示const引用(即int& const);实际上没有const引用,(注意不是constconst int&的引用),references初始化后不能反弹。在这种情况下,const 限定符将被忽略。

Reference types cannot be cv-qualified at the top level; there is no syntax for that in declaration, and if a qualification is added to a typedef-name or decltype specifier, or type template parameter, it is ignored.

我总是建议人们采用 East-const 的风格,总是将 const 放在它所限定的右边(东边),而你恰恰遇到了一个完全不同的情况.

使用 West-const 表示法,您写了:

template<typename T>
void modify(const T &j){ j = 42; } // j has type int&

int&替换为T,你天真地进行了文本替换,就好像它是一个宏,思考 j作为 const int && j。而事实并非如此。

使用 East-const 表示法,你会写:

template<typename T>
void modify(T const& j){ j = 42; }

int& 替换为 T,您将得到 int& const& j:请注意 const 与您想象的完全不同?

现在,世界又变得有意义了。您有一个对 int:

的引用的常量引用
  • 参考是 const,所以您不能修改参考本身...但是您 永远 不能。
  • int 不是 const,因此您可以修改 int

CQFD.