C++中传递右值引用绑定的临时对象无法编译的原因
问题根因
编译错误的核心是混淆了变量的声明类型和表达式的值类别两个完全独立的C语法规则。
C标准有一条非常容易踩坑的明确规定:所有有名字的实体(包括声明为右值引用类型的变量),当直接使用变量名作为表达式时,该表达式的值类别永远是左值。
验证代码如下:
#include <utility> int main() { struct dummy {}; const auto &c = [](dummy &&){}; dummy &&d = dummy{}; c(dummy{}); c(std::move(d)); // c(d); // 无法编译 [](auto &&){}(d); // 可正常编译 }
逐现象解释
- 直接传递
dummy{}可编译dummy{}是没有名字的临时对象,对应表达式是纯右值,符合非const右值引用dummy&&的绑定规则,因此可以正常传参。 - 直接传
d编译失败,传std::move(d)可编译
你声明的d虽然类型是dummy&&(右值引用,作用是绑定临时对象、延长其生命周期),但d是有名字的变量,直接写d作为参数时属于左值表达式。而非const右值引用不允许绑定左值,这就是报错信息里cannot bind rvalue reference of type 'main()::dummy&&' to lvalue of type 'main()::dummy'的直接来源。std::move的作用就是将输入表达式无条件强制转换为对应类型的右值引用,转换后的亡值(右值的一类)可以正常绑定到dummy&&参数,因此可以编译通过。 - 传
d给接收auto&&的泛型lambda可编译
这里的auto&&不是固定的右值引用类型,而是转发引用(万能引用):它会根据传入表达式的值类别自动推导参数类型——传入左值时推导为左值引用dummy&,传入右值时推导为右值引用dummy&&。传入左值d时参数被推导为左值引用,完全符合左值绑定规则,因此可以编译,并不是编译器将d识别成了右值。
关于clang-tidy提示的说明
clang-tidy提示对可平凡拷贝类型使用
std::move没有实际效果
这个提示是从性能收益角度给出的建议:对于空结构体、基础int类型这类拷贝/移动没有任何额外开销、属于平凡可复制类型的对象,不管是传值、传左值还是传右值,最终编译器生成的代码没有性能差异,std::move不会带来任何优化收益,并非指此处std::move在语法层面无效。
内容的提问来源于stack exchange,提问作者Sergey Kolesnik
相关产品推荐
相关产品推荐

