【问题标题】:Compiler doesn't fail when pushing back a std::unique_ptr into a std::vector将 std::unique_ptr 推回 std::vector 时编译器不会失败
【发布时间】:2019-11-17 10:29:55
【问题描述】:

unique_ptr 不能被推回std::vector,因为它是不可复制的,除非使用std::move。但是,如果让F 是一个返回unique_ptr 的函数,那么std::vector::push_back(F()) 操作是允许的。下面有一个例子:

#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  v.push_back(p1); // (1) This fails, should use std::move

  v.push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}

(2) 是允许的,但(1) 是不允许的。这是因为返回的值以某种方式隐式移动了吗?

(2)中,真的有必要使用std::move吗?

【问题讨论】:

    标签: c++ smart-pointers stdvector move-semantics


    【解决方案1】:

    std::move(X) 本质上的意思是“在这里,把 X 当作一个临时对象”。

    create() 返回一个临时的std::unique_ptr&lt;A&gt; 开头,所以move 是不必要的。


    如果您想了解更多信息,请查看value categories。您的编译器使用值类别来确定表达式是否引用临时对象(“rvalue”)或不(“lvalue”)。

    p1 是左值,create() 是右值。

    【讨论】:

    • 虽然正确,但您的答案缺少解释push_back 允许这种行为的原因和方式的部分,这部分可以帮助人们了解如何实现并允许此类代码与他们自己的 API 一起使用
    【解决方案2】:

    std::vector::push_back() 有一个将右值引用作为输入的重载:

    void push_back( T&& value );
    

    create() 的返回值是一个未命名的临时值,即一个右值,因此可以将其原样传递给push_back(),而无需在其上使用std::move()

    std::move() 仅在传递命名变量(即左值)时才需要,其中需要右值。

    【讨论】:

      【解决方案3】:

      在 C++11 中,我们得到了移动构造函数和右值语义。

      std::move(X) 只是转换为右值,它将 X 转换为 X&& 就是这样。与 move ctor 相比,move 构造函数通常会“窃取”参数持有的资源。 unique_ptr 有一个移动 ctor。

      函数返回值已经是一个右值(除非函数返回一个左值引用,如 cmets 中的 @HolyBlackCat 所示),它将触发移动 ctor 而无需任何额外的强制转换。并且由于为 unique_ptr 定义了 move ctor,它将编译。

      v.push_back(p1); 失败的原因也是:您尝试使用左值调用复制构造函数,但它失败了,因为 unique_ptr 没有复制 ctor。

      【讨论】:

        【解决方案4】:

        同样值得知道的是,由于编译器能够移动未显式移动的对象 (NRVO),它也可以工作

        #include <iostream>
        #include <vector>
        #include <memory>
        
        class A {
          public:
            int f() { return _f + 10; }
        
          private:
            int _f = 20;
        };
        
        std::unique_ptr<A> create() {
            std::unique_ptr<A> x (new A);
            return x; 
        
        }
        
        
        int main() {
          std::unique_ptr<A> p1(new A());
        
          std::vector< std::unique_ptr<A> > v;
        
          //v.push_back(p1); // (1) This fails, should use std::move
        
          v.push_back(create()); // (2) This doesn't fail, should use std::move?
        
          return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 2014-05-12
          • 2019-11-07
          • 1970-01-01
          • 2014-12-27
          • 2021-05-21
          • 2015-07-23
          • 1970-01-01
          • 2022-10-21
          • 2018-02-02
          相关资源
          最近更新 更多