高效/快速复制标准容器,如std :: vector

时间:2012-09-30 16:54:24

标签: c++ vector copying processing-efficiency

对于自定义用途,我已将std::vector继承到自定义class Vector。根据我的要求public inheritance is ok

一个目的是避免多次复制矢量数组,因此我认为这个自定义类是基于'所有权'。

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // vA2 refers to original A[100] and ...
                     // ... vA1 is now blank which is expected

这就是它为C ++ 03实现的方式:

template<typename T>
struct Vector : std::vector<T>
{
  // ... other constructors

  Vector (const Vector &copy) // copy constructor
  {
    this->swap(const_cast<Vector&>(copy)); // <---- line of interest
  }
  // 'operator =' is TBD (either same as above or unimplemented)
};

我是否违反任何语言规则/功能/惯例?这段代码有什么不好的吗?

修改:我在下面的回答中添加了我的新方法(这是它的工作demo)。

4 个答案:

答案 0 :(得分:4)

我认为你要找的是移动语义。

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = std::move(vA1); // vA2 refers to original A[100] and ...
                 // ... vA1 is now blank which is expected

如果您的编译器没有移动语义支持,我建议您使用智能指针或查看boost::move,它可以模拟前C ++ 11编译器中的移动语义..

std::shared_ptr<Vector<A>> vA1(new Vector<A>(100)); // vA1 is allocated A[100]
std::shared_ptr<Vector<A>> vA2 = vA1; // vA2 refers to original A[100] and ...
vA1.reset();
                 // ... vA1 is now blank which is expected

甚至更简单:

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2;
vA1.swap(vA2);// vA2 refers to original A[100] and ...
                 // ... vA1 is now blank which is expected

答案 1 :(得分:3)

您应该真正了解一下使用C ++ 11:所有标准容器都是可移动的,因此在很多情况下都是类似于您的语义,即当知道所有权可以转移时。更准确地说,当从函数返回对象或使用临时构造或分配对象时,容器会被移动而不是被复制。试图以某种方式复制这种机制几乎肯定会在可能的情况下产生意外行为。

在C ++ 11中,可移动语义被集成到语言和函数中,包括构造函数和赋值,可以重载以根据对象是否可移动而表现不同。

答案 2 :(得分:2)

您的代码违反了支持const-correctness的约定:

const Vector<int> foo(12);
const Vector<int> bar(foo); // UB.
some_function_that_takes_Vector_by_value(bar); // UB.

您可能会说,“但是没有人会创建const Vector实例,因此UB永远不会发生”。在这种情况下,您至少可以使您的副本ctor采用非const引用,以遵循函数不应修改const&参数所采用的对象的约定。

即便如此,我个人还是希望用户通过显式交换来“开启”速度,而不是为他们创建这样的陷阱。我也更喜欢没有复制ctor,而不像普通复制那样。如果人们不得不记住Vector实际上无法复制,那么他们就不应该使用复制语法来复制它。

C ++ 03中不必要的副本问题不是新的问题,IMO不需要新颖的解决方案。许多C ++程序员已经知道如何利用swap和复制elision,那些不知道如何避免不必要的副本的人最好不要学习以正常的方式去做,而不是学会以独特的方式去做你的一个特定班级所要求的。

答案 3 :(得分:1)

凭借评论/答案的洞察力,我意识到我当前的方法不是处理事情的好主意 我想出了一个改进的设计,我尝试使用swap()方法模拟“移动”操作(在这种情况下认为它们可以互换)。

对于想要更快速复制(即通过交换移动)的任何类或容器,应继承下面的类(直接从真实文件中复制粘贴):

  /*
   * An empty CRTP base class for the classes which are allowing move sematics (effectively swap)
   * A given class `X` must implement `void swap(X&);` method for the movement (i.e. content swapping)
   * For example, `X` is deriving this class (i.e. `class X : public Movable<X> ...`)
   * ... below is an illustration for how to transfer contents of an `X` object to another
   * `X o1; ... X o2(o1.move()); // contents of o1 is now moved to o2`
   */
  template<typename Derived>
  class Movable
  {
    /*
     * Empty constructor for normal behavior
     */
    public: Movable ()
            {}

    /*
     * Copy constructor which does nothing; without this compiler errors out !
     */
    public: Movable (const Movable &copy)
            {}

    /*
     * Move constructor which will effectively call `Derived::swap()` method
     * After this, the contents of the object will be moved to each other
     * For syntactic sugar, it has been made `explicit`
     */
    public: explicit Movable (Movable &orig)
            {
              (*this)(orig);
            }

    /*
     * Moving while Not initializing object, one need to use `operator ()` and not `operator =`
     * If `operator =` is used then simply `move()` part will not happen; i.e. below 2 are same:
     * `obj1 = obj2.move();` and `obj1 = obj2;`
     */
    public: void operator () (Movable &orig)
            {
              static_cast<Derived*>(this)->swap(static_cast<Derived&>(orig));
            }
    /*
     * This method is called from `Derived` class when move sematics is intended
     */
    public: Movable& move ()
            {
              return *this;
            }
  };

这里应该如何部署:

template<typename T>
struct Vector : std::vector<T>,
                Movable<Vector<T> >  // inherit as CRTP base
{
  // ... other methods
  typedef Movable<Vector<T> > Movable_;
  Vector (Movable_ &orig) : Movable_(orig) {}  // Declare a constructor
};

这里应该如何使用它:

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // normal copying (no change)
vA2 = vA1; // normal assignment (no change)
Vector<A> vA3(vA1.move()); // <------- "moves" contents from 'vA1' to 'vA3'
vA1(vA2.move()); // <------- "moves" contents from 'vA2' to 'vA1'
vA2 = vA3.move(); // normal copying from 'vA3' to 'vA2' ('vA3' unaffected)