给定此代码:
#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 &j
?const
会怎样?
没有引用这样的东西,即没有T & &
。
给定一个T
int&
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&
,const
在T
本身上是限定的。当T
int&
时,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
,你天真地在执行文本替换,就好像它是一个宏一样,并认为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。