我写了一个测试程序:
#include <iostream>
#include <type_traits>
using namespace std;
template<class T>
void f(T&& t)
{
cout<<is_const<T>()<<endl;
//++t;
}
int main() {
const int i=0;
f(i);
return 0;
}
输出“0”,显示T
不是常量!这很奇怪。然后我修改了f
:
template<class T>
void f(T&& t)
{
cout<<is_const<T>()<<endl;
++t;
}
然后出现编译错误,说我们正在修改只读t
。
那么t
是否可以修改?我的计划中有任何错误的假设吗?
答案 0 :(得分:14)
请参阅std::is_const:
如果
T
是const限定类型(即const或const volatile),则提供成员常量值等于true。对于任何其他类型,值为false。
t
被声明为forwarding references。因此,对于您的代码,T
将推断为const int&
,这是一个参考。引用不能是 const-qualified ,它不是const本身。确切地说,没有 const引用(即int& const
),因为引用不能再次反弹。 const int&
是{em>到const int
的引用;并注意t
因此无法修改。
从标准$8.3.2/1 References [dcl.ref]
开始除了cv限定符之外,Cv限定的引用是不正确的 是通过使用typedef-name([dcl.typedef], [temp.param])或decltype-specifier([dcl.type.simple]),在这种情况下 cv限定符被忽略。
来自cppreference的更多示例:
std::cout << std::is_const<int>::value << '\n'; // false
std::cout << std::is_const<const int>::value << '\n'; // true
std::cout << std::is_const<const int*>::value << '\n'; // false
std::cout << std::is_const<int* const>::value << '\n'; // true
std::cout << std::is_const<const int&>::value << '\n'; // false
答案 1 :(得分:4)
t
是否可修改取决于T
的类型,该类型是根据传入的变量类型推断出来的。在这种情况下,您传入的是const int
,所以t
的类型为const int &
,因为您将其视为转发参考。
至于为什么is_const
返回false,那是因为T
引用类型和引用永远不是const。
答案 2 :(得分:3)
您的模板功能(即f
)将forwarding reference(a.k.a 通用参考)作为参数。决定扣除T
的规则被称为reference collapsing rules。这些规则总结如下:
T&
&
变为T&
T&
&&
变为T&
T&&
&
变为T&
- 醇>
T&&
&&
变为T&&
现在,根据参考折叠规则,当您作为参数提供给f
int const i
时,T
将被扣除到int const&
。
根据C ++标准表52,如果is_const
true
符合条件, T
将评估为const
。
此外,在C ++标准§20.13.4.3/ p5类型属性[meta.unary.prop] 中,有以下is_const
类型特征如何工作的示例:
[实施例:
is_const<const volatile int>::value // true is_const<const int*>::value // false is_const<const int&>::value // false is_const<int[3]>::value // false is_const<const int[3]>::value // true
- 结束示例]
正如您在第三行所看到的那样is_const
评估为false
。为什么?因为传递给is_const
作为模板参数的类型是引用类型。现在,引用本质上是const
,因为你不能改变它们所引用的内容,但它们不是const
合格的。因此,带引用类型的is_const
将评估为false
。
答案 3 :(得分:0)
顶级答案是引用不具备cv资格。只有他们引用的类型才能获得cv认证。
但这是const
这个词的位置重要的时候之一。当你打电话:
template<class T>
void f(T&& t)
左键为const int
,T
推导出什么?您可能会说const int&
(这是正确的),但看起来该类型为const (int&)
。因此可能会混淆T
const
。
但如果相反你说它推断为int const&
(与以前类型相同),这里没有可能的混淆,为什么std::is_const<int const&>
可能不那么令人惊讶是std::false_type
。
答案 4 :(得分:0)
可能的解决办法可能是在重载中拦截const类型:
template<class T>
void f(T&& t)
{
++t;
}
template<class T>
void f(const T& t)
{
std::cout << "const here" << std::endl;
}
然后对const对象的引用将由第二个函数处理。