【问题标题】:Move construction from const reference从 const 引用移动构造
【发布时间】:2013-05-20 15:55:10
【问题描述】:

我有以下情况,我需要从 t1 移动构造 t2。 不幸的是,这是不可能的(我想是违反常数的)
从 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; 
}

【问题讨论】:

  • 如果你需要从t1移动-构造t2,你为什么不做T t2{std::move(t1)}
  • foo的目的是什么?
  • 查看更新以获取更具体的示例
  • @3XX0:那么,如果你想离开它,为什么要引用const?取一个T&&,让调用者做foo(std::move(t1))

标签: c++ c++11 move move-semantics


【解决方案1】:

从 foo 的调用者透明地处理该问题的正确方法是什么?

你不能那样做,这是故意的。左值永远不会透明地移动。

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

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

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

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

请注意,一旦您将诸如foo() 之类的无意义名称转换为反映某些具体程序中某些特定操作的语义的名称,最后的建议可能不正确。但是,在不知道该操作的含义的情况下,我只能就如何使您的代码 sn-p 编译以及如何考虑一般的移动语义提供正式的机械建议。

【讨论】:

    【解决方案2】:

    如果您非常想这样做,您可以这样做。要做到这一点,您的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 版本juststd::move 做同样的事情是没有意义的,但一个版本也对输入进行了一些其他处理(并且,无论出于何种原因,不能'not be used with std::move) 如果您真的别无选择,可能会勉强接受。

    【讨论】:

    • 感谢您的完美回答 :) 确实,在压制所有代码之后,这似乎很愚蠢......
    【解决方案3】:

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

    C++ 不允许您在没有至少其中之一的情况下移动东西。这是设计

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

    这样,当您扫描代码时,您可以查看是否不小心以不正确的方式使用了已移动的值,而无需看到除了明确使用 std::move 之外的任何内容。

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

    【讨论】:

      猜你喜欢
      • 2012-06-01
      • 2015-05-25
      • 2011-09-13
      • 2017-05-23
      • 2020-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-22
      相关资源
      最近更新 更多