std::remove_reference的作用是什么?C++14泛型Lambda中B行报错原因
std::remove_reference的作用及泛型lambda里的引用坑 先直接说std::remove_reference的用处:它是C++标准库给我们的类型萃取小工具,最核心的作用就是剥离类型里的引用修饰符——不管是左值引用(&)还是右值引用(&&),它都能给你返回不带引用的原始类型。举个例子:
- 给它
int&,它返回int - 给它
const double&&,它返回const double
接下来咱们拆解你代码里的问题:
为什么// B行会报错?
先看你的泛型lambda定义:
auto selfincr = [] (auto & value) { ... };
这里的value是左值引用类型——当你调用selfincr(f)时,decltype(value)得到的是float&;调用selfincr(i)时,得到的是int&。
再看// B行的代码:
value = incr<decltype(value)>(value);
你把decltype(value)(也就是引用类型,比如float&)传给了incr的模板参数T。此时incr会被实例化成这样:
float& incr(float& v) { return v + 1; }
问题就出在return v + 1这一步:v + 1计算后得到的是一个临时的float对象(也就是右值),但函数的返回类型是float&(左值引用)。C++明确禁止把左值引用绑定到临时右值上——这不仅会触发编译错误,就算能通过编译,也会指向一个马上销毁的对象,完全不安全。
std::remove_reference在这里解决了什么问题?
再看// A行的代码:
value = incr<std::remove_reference<decltype(value)>>(value);
(小提示:严格来说,这里应该用std::remove_reference_t<decltype(value)>或者std::remove_reference<decltype(value)>::type来拿到剥离引用后的类型,不过C++14及以后的编译器大多支持简化写法,核心逻辑是一样的)
std::remove_reference会把decltype(value)的引用修饰符彻底去掉:比如float&变成float,int&变成int。这时候incr会被实例化成我们预期的样子:
float incr(float v) { return v + 1; }
这个版本的incr接收值类型参数,返回的也是值类型——v + 1返回的临时float对象可以正常被复制,然后赋值给value(左值引用)完全没问题,程序就能正确运行了。
简单总结:std::remove_reference在这里帮我们把lambda参数的引用类型转回了原始值类型,避免了因为传递引用类型给模板函数而导致的“返回值引用绑定临时对象”的编译错误。
内容的提问来源于stack exchange,提问作者Patrizio Bertoni

