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

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

本文关键字:引用 恒定性 失去 常量 为什么      更新时间:2023-10-16

给定此代码:

#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 & &

给定一个Tint&const T&,该类型折叠为int&

康斯特会发生什么?

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

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

[dcl.ref] 如果 typedef-name ([dcl.typedef], [temp.param]( 或 decltype-specifier ([dcl.type.simple]( 表示对类型 T 的引用的类型 TR,则尝试创建类型"对 cv TR 的左值引用"将创建类型"对 T 的左值引用",而尝试创建类型"对 cv TR 的 rvalue 引用"将创建类型 TR。 [ 注意:此规则称为引用折叠。 — 尾注 ]

这里的 cv 是指 cv 限定符,即常量和易失性。

要阐明为什么这适用于模板参数,请执行以下操作:

[temp.param] 标识符不遵循省略号的类型参数将其标识符定义为 typedef-name ...


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

给定const T&constT本身上是限定的。当Tint&时,const T表示const引用(即int& const(;实际上没有const引用,(请注意,它不是对const的引用,即const int&(,引用在初始化后无法重新绑定。在这种情况下,const限定符将被忽略。

引用类型不能在顶级进行 cv 限定;声明中没有语法,如果将限定添加到 typedef-name 或 decltype 说明符或类型模板参数中,则会忽略该限定符。

我总是建议人们采用 East-const 风格,即始终将const放在它所限定的右侧(东部(,而您只是遇到了它会产生一切差异的情况。

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

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

int&代替T,你天真地在执行文本替换,就好像它是一个宏一样,并认为jconst 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。