从const引用中移动构造

Move construction from const reference

本文关键字:移动 const 引用      更新时间:2023-10-16

我有以下情况,我需要从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; 
}

从foo的调用者那里透明地处理它的正确方法是什么?

你不能那样做,这是有意为之的。左值永远不会从透明中移动。

由于移出对象通常处于未知(但合法)状态,如果客户端可以将左值作为参数传递给函数,并且允许该函数静默地从它移出,这将是危险的。客户端可能会在不知情的情况下留下一个僵尸对象!

因此,该语言强制执行这样的规则:从左值移动必须是有意识的动作,并且必须通过调用std::move()显式地将左值转换为右值(实际上是xvalue)来实现。

在这种情况下,我会说你的函数foo() -无论它实际上应该做什么-应该接受一个右值引用,客户端应该像这样调用它:

T t2 = foo(std::move(t1)); 

注意,最后一个建议可能是不正确的,一旦你把一个没有意义的名字,如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; 
}
现在,尽管你可以/可以这样做,其他的答案或多或少仍然是正确的——即使你可以这样做,你几乎可以肯定不应该。如果你在一个支持移动构造的真实类上使用这个foo(即,将真正地将状态从源移动到目标),你最终会基本上破坏你的源对象,即使它仍然可见。

对你编辑过的问题这样做,foo可以,但并不总是,破坏源对象将是特别是有害的。使用正确的名称可以清楚地表明foo确实从源代码移动,它可以这样做可能只是合理的—但是(至少对我来说)一个可能会或可能不会销毁其参数的函数似乎比可靠地销毁其参数的函数要糟糕得多。显然,像上面这样的foo版本,只是做与std::move相同的事情是没有意义的,但是如果一个版本还对输入进行了一些其他处理(并且,无论出于何种原因,不能与std::move一起使用),那么如果您真的没有任何其他选择,可能只是勉强可以接受。

不需要传递值和显式std::move

c++不允许你在没有这些条件的情况下移动东西。这是设计

c++中移动的基本规则是不允许偶然发生。如果你有一个左值,并且你想把它传递给一些可能从它移动的函数,那么你作为调用者必须使用std::move。这是经过设计的,因此很明显,调用者的意图是将对象从。

移动。

这样,当您扫描代码时,您可以看到是否意外地以不正确的方式使用了移出值,而不必看到任何比显式使用std::move更多的东西。

你想要的不是你应该要的。