通用引用类型推导时const修饰符是否会被丢弃?
通用引用类型推导中const的处理问题
问题代码
#include <iostream> // std::cout #include <type_traits> template <class T> // deductions among universal reference void fun(T &&b) // fold reference, maintain const { std::cout << b << " "; std::cout << std::is_const<T>::value << " "; std::cout << std::is_lvalue_reference<T>::value << " "; std::cout << std::is_rvalue_reference<T>::value << std::endl; } int main() { int a = 1; fun(a); // lvalue: T=int& fun(std::move(a)); // rvalue: T=int const int ca = 1; fun(ca); // const_lvalue: T=int& (why no const?) fun(std::move(ca)); // const_rvalue: T=const int int &la = a; fun(la); // lvalue_ref: T=int& int &&ra = 1; fun(ra); // rvalue_ref: T=int& (r_ref is an l_val) fun(std::forward<decltype(ra)>(ra)); // rvalue_ref + perfect forwarding T=int const int &cla = a; fun(cla); // const_lvalue_ref: T=int& (no const?) const int &&cra = 1; fun(cra); // const_rvalue_ref: T=int& (no const?) return 0; }
运行结果
1 0 1 0 1 0 0 0 1 0 1 0 <-const is discarded //const_lvalue: T=int& 1 1 0 0 <-const is maintained //const_rvalue: T=const int 1 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0 <-const is discarded //const_lvalue_ref: T=int& 1 0 1 0 <-const is discarded //const_rvalue_ref: T=int&
cppinsights生成的模板特化
/* First instantiated from: insights.cpp:18 */ #ifdef INSIGHTS_USE_TEMPLATE template<> void fun<int &>(int & b) { } #endif /* First instantiated from: insights.cpp:19 */ #ifdef INSIGHTS_USE_TEMPLATE template<> void fun<int>(int && b) { } #endif /* First instantiated from: insights.cpp:22 */ #ifdef INSIGHTS_USE_TEMPLATE template<> void fun<const int &>(const int & b) { } #endif /* First instantiated from: insights.cpp:23 */ #ifdef INSIGHTS_USE_TEMPLATE template<> void fun<const int>(const int && b) { } #endif
核心疑问
为什么代码运行结果显示左值实参的const被丢弃,但cppinsights的推导结果却保留了const?通用引用类型推导时const到底会不会被丢弃?
问题解答
1. 通用引用的推导规则(正确结论)
通用引用(T&&)的类型推导遵循以下规则:
- 当实参是左值(包括const左值、左值引用、命名的右值引用)时,模板参数
T会被推导为左值引用类型:- 若实参是
const int左值,T推导为const int&; - 若实参是
int左值,T推导为int&。
- 若实参是
- 当实参是纯右值/将亡值(比如
std::move后的右值、临时对象)时,模板参数T会被推导为非引用类型:- 若实参是
const int右值,T推导为const int; - 若实参是
int右值,T推导为int。
- 若实参是
简言之:通用引用推导不会丢弃const,const会被保留在推导后的T类型中。
2. 代码运行结果与cppinsights的差异原因
差异出在你代码中判断const的方式——std::is_const<T>::value的检测逻辑不符合预期:
- C++中引用本身不能被const修饰,
const int&本质是“指向const int的左值引用”,而非“const的左值引用”。因此std::is_const<const int&>::value的结果是false,因为它只检测T本身是否是const类型,不会穿透引用去检测指向的类型。 - cppinsights显示的特化类型是正确的:比如调用
fun(ca)时,T确实是const int&,只是你的代码用std::is_const<T>无法检测到引用指向的const属性。
3. 正确检测const的方式
要检测T对应的底层类型是否带const,需要先移除引用属性,再判断:
std::is_const<std::remove_reference_t<T>>::value
修改你的fun函数后,就能正确输出const属性:
template <class T> void fun(T &&b) { std::cout << b << " "; std::cout << std::is_const<std::remove_reference_t<T>>::value << " "; std::cout << std::is_lvalue_reference<T>::value << " "; std::cout << std::is_rvalue_reference<T>::value << std::endl; }
此时调用fun(ca)的输出会变为1 1 1 0,和cppinsights的推导结果一致,证明const并没有被丢弃。
内容的提问来源于stack exchange,提问作者pikalee
相关产品推荐
相关产品推荐

