揭开 std::move 转换魔法的神秘面纱
当调用 std::move() 时,令人费解的是观察到引用的右值参数可以绑定到左值,通常限制左值附加到右值引用。深入研究 std::move() 的实现揭示了这个明显悖论的关键。
剖析 std::move() 实现
从精炼的开始std::move() 的版本:
template <typename T> typename remove_reference<T>::type&& move(T&& arg) { return static_cast<typename remove_reference<T>::type&&>(arg); }
情况 1:使用右值调用 move()
当 move() 与右值一起使用时,例如临时对象:
Object a = std::move(Object()); // Object() is temporary, hence prvalue
生成的模板实例化为:
remove_reference<Object>::type&& move(Object&& arg) { return static_cast<remove_reference<Object>::type&&>(arg); }
由于remove_reference将T&转换为T或T&&转换为T,并且Object是一个普通值,所以最终的函数形态变为:
Object&& move(Object&& arg) { return static_cast<Object&&>(arg); }
强制转换至关重要,因为命名的右值引用被视为左值。
情况 2:使用左值调用 move()
当使用左值调用 move() 时:
Object a; // a is an lvalue Object b = std::move(a);
生成的 move() 实例化为:
remove_reference<Object&>::type&& move(Object& && arg) { return static_cast<remove_reference<Object&>::type&&>(arg); }
再次,remove_reference 将 Object& 转换为 Object,产生:
Object&& move(Object& && arg) { return static_cast<Object&&>(arg); }
引用折叠的本质
理解 Object& && 及其绑定左值的能力是解开谜团的关键。 C 11 引入了引用折叠的特殊规则,其中规定:
Object & && = Object & Object & &&& = Object & Object && & = Object & Object && &&& = Object &&
因此,Object& && 实际上转换为 Object&,一个可以轻松绑定到左值的典型左值引用。
结果函数
使用这些规则,最终函数将变为:
Object&& move(Object& arg) { return static_cast<Object&&>(arg); }
镜像右值的实例化,它将其参数转换为右值引用,从而确保统一的行为.
remove_reference 的重要性
当检查替代函数时,remove_reference 的目的变得显而易见:
template <typename T> T&& wanna_be_move(T&& arg) { return static_cast<T&&>(arg); }
当用左值实例化时:
Object& && wanna_be_move(Object& && arg) { return static_cast<Object& &&&>(arg); }
应用引用折叠规则揭示了一个不可用的类似移动的函数,为左值参数返回左值。罪魁祸首是缺少remove_reference,它阻碍了正确转换为右值引用。
以上是当右值引用应该只绑定到右值时,std::move() 如何绑定到左值?的详细内容。更多信息请关注PHP中文网其他相关文章!