3XX*_*XX0 5 c++ move move-semantics c++11
我有以下情况需要从t1移动构造t2.不幸的是,我不可能这样做(我认为
是constness违规)从foo的调用者那里透明地处理它的正确方法是什么?(即不需要传递值和显式的std :: move)
struct T
{
T() = default;
~T() = default;
T(T&&) = default;
};
T foo(const T& t)
{
T t3;
if (predicate)
return t3;
else
return std::move(t);
}
int main()
{
T t1;
T t2 = foo(t1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
从foo的调用者那里透明地处理它的正确方法是什么?
你不能这样做,而那是打算这样做的.左值永远不会透明地移动.
由于移动的对象通常处于未知(但合法)状态,如果客户端可以将左值作为参数传递给函数,并且允许该函数以静默方式从其中移动,那将是危险的.客户端可能会留下一个僵尸对象,甚至不知道它!
因此,该语言强制执行从左值移动必须是有意识的动作的规则,并且应该通过调用来明确地将左值变为右值(实际上是x值)std::move().
在这种情况下,我会说你的函数foo()- 无论它实际应该做什么 - 应该采用右值引用,客户端应该像这样调用它:
T t2 = foo(std::move(t1));
Run Code Online (Sandbox Code Playgroud)
请注意,一旦您将无意义的名称foo()转换为反映某些具体操作中某些特定操作的语义的内容,这最后的建议可能不正确.然而,在不知道该操作的含义的情况下,我只能提供有关如何编译代码片段的正式机械建议,以及如何考虑移动语义.
如果你非常想要的话,你可以做到这一点。为此,您foo最终将本质上重新实现std::move.
struct T
{
T() = default;
~T() = default;
T(T&&) = default;
};
template<class T> struct my_remove_reference {typedef T type;};
template<class T> struct my_remove_reference<T&> {typedef T type;};
// The third part of a normal `remove_reference`, but which we don't need in
// this case (though it'll all still be fine if you un-comment this).
//template<class T> struct my_remove_reference<T&&> {typedef T type;};
template <typename T>
typename my_remove_reference<T>::type &&foo(T &&t) {
return static_cast<typename my_remove_reference<T>::type &&>(t);
}
int main()
{
T t1;
T t2 = foo(t1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在,尽管事实上你可以/可以做到这一点,但其他答案或多或少仍然是正确的——即使你可以做到,你几乎肯定不应该这样做。如果你用过这个foo在支持移动构造的真实类上使用它(即,真正将状态从源移动到目的地),您最终基本上会破坏源对象,即使它仍然可见。
对您编辑的问题执行此操作,在哪里foo可以(但并不总是)破坏源对象将特别有害。有了正确的名称,就可以清楚地表明真正foo从源头开始,这样做可能只是勉强合理——但是(至少对我来说)一个可能会或可能不会破坏其论点的函数似乎比一个可靠地这样做的人。显然,像上面那样做同样事情的版本是没有意义的,但是对输入也做了一些其他处理的版本(并且,无论出于何种原因,不能与 一起使用)可能只是如果你真的没有其他选择的话,勉强可以接受。foostd::movestd::move