【问题标题】:move constructors for vectors of shared_ptr<MyClass>移动 shared_ptr<MyClass> 向量的构造函数
【发布时间】:2013-12-13 07:40:00
【问题描述】:

我了解如果您希望传递 MyClass 对象的向量并且它是一个临时变量,如果为 MyClass 定义了一个移动构造函数,那么它将被调用,但是如果您传递一个向量 boost::shared_ptr&lt;MyClass&gt;std::shared_ptr&lt;MyClass&gt;? shared_ptr 是否有一个移动构造函数,然后调用 MyClass 的移动构造函数?

【问题讨论】:

    标签: c++ boost c++11 move shared-ptr


    【解决方案1】:

    如果为 MyClass 定义了一个移动构造函数,那么它将被调用

    通常不会。移动向量通常是通过转移托管数组的所有权来完成的,而将移动的向量留空。物体本身没有被触及。 (我认为如果两个向量的分配器不兼容,可能会有一个例外,但这超出了我需要处理的任何事情,所以我不确定那里的细节)。

    shared_ptr 是否有一个移动构造函数,然后调用 MyClass 的移动构造函数?

    没有。同样,它有一个移动构造函数,它将MyClass 对象的所有权转移给新指针,而旧指针为空。对象本身没有受到影响。

    【讨论】:

      【解决方案2】:

      是的,std::shared_ptr&lt;T&gt; 有一个移动构造函数,以及一个可以从相关共享指针移动的模板化构造函数,但它根本不接触托管对象。新构造的共享指针共享托管对象的所有权(如果有的话),并且移出的指针被解除(“null”)。

      例子:

      struct Base {};                // N.B.: No need for a virtual destructor
      struct Derived : Base {};
      
      auto p = std::make_shared<Derived>();
      std::shared_ptr<Base> q = std::move(p);
      
      assert(!p);
      

      【讨论】:

      • 它有一个常规的移动构造函数和模板,因为模板不能用作移动或复制构造函数。
      • @MikeSeymour:参考?
      • C++11 20.7.2.2 for shared_ptr。更一般地说,12.8/3:“类 X 的 非模板 构造函数是移动构造函数,如果...”
      • @MikeSeymour:谢谢。我在 20.7.2.2.1 中没有看到它,但它与模板版本一起存在。
      • 不就是UB吗,因为Base没有虚拟析构函数?为什么不需要它?
      【解决方案3】:

      如果您的意思是移动std::vector&lt;std::shared_ptr&lt;MyClass&gt;&gt;。那么即使std::shared_ptr 的移动构造函数也不会被调用。因为移动操作是直接在std::vectorlevel上进行的。

      例如,std::vector&lt;T&gt; 可以实现为指向T 数组的指针和size 成员。这个的移动构造函数可以实现为:

      template <typename T>
      class vector {
      public:
          /* ... other members */
          vector(vector &&another): _p(another._p), _size(another._size) {
              /* Transfer data ownership */
              another._p = nullptr;
              another._size = 0;
          }
      
      private:
          T *_p;
          size_t _size;
      }
      

      可以看到,在这个过程中,T 类型的数据成员根本没有被触及。

      编辑:更特别的是 C++11 标准:§23.2.1。通用容器要求 (4)有一个表格包含通用容器的实现要求,其中包含以下要求:

      X是元素的类型,u是标识符声明,rv是右值引用,aX类型的容器)

      X u(rv)
      X u = rv
      

      C++ 标准:这两个(移动构造函数)对于除std::array 之外的所有标准容器应该具有恒定的时间复杂度。

      因此很容易得出结论,实现必须使用我在上面指出的 std::vector 的移动构造函数的方式,因为它不能调用单个元素的移动构造函数,否则时间复杂度将变为线性时间。

      a = rv
      

      C++ 标准:a 的所有现有元素要么被移动分配,要么被销毁 a 应等于 rv 在此分配之前的值。

      这是用于移动赋值运算符。这句话只说明a 中的原始元素应该“正确处理”(移动分配或销毁)。但这不是一个严格的要求。恕我直言,实现可以选择最合适的方式。

      我还查看了 Visual C++ 2013 中的代码,这是我找到的 sn-p(vector 标头,从第 836 行开始):

      /* Directly move, like code above */
      void _Assign_rv(_Myt&& _Right, true_type)
      {   // move from _Right, stealing its contents
          this->_Swap_all((_Myt&)_Right);
          this->_Myfirst = _Right._Myfirst;
          this->_Mylast = _Right._Mylast;
          this->_Myend = _Right._Myend;
      
          _Right._Myfirst = pointer();
          _Right._Mylast = pointer();
          _Right._Myend = pointer();
      }
      
      /* Both move assignment operator and move constructor will call this */
      void _Assign_rv(_Myt&& _Right, false_type)
      {    // move from _Right, possibly moving its contents
          if (get_allocator() == _Right.get_allocator())
              _Assign_rv(_STD forward<_Myt>(_Right), true_type());
          else
              _Construct(_STD make_move_iterator(_Right.begin()),
              _STD make_move_iterator(_Right.end()));
      }
      

      在这段代码中,操作很清楚:如果thisright 操作数具有相同的分配器,它将直接窃取 内容,而不对单个元素进行任何操作。但如果没有,则将调用单个元素的移动操作。目前,其他答案适用(std::shared_ptr 的东西)。

      【讨论】:

      • 我以为每个元素都被移动了?
      • @user997112,我怀疑。
      猜你喜欢
      • 2021-09-07
      • 1970-01-01
      • 2017-06-11
      • 2017-09-24
      • 1970-01-01
      • 2014-11-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      相关资源
      最近更新 更多