1

We all knows that a std::vector will increase it's capacity dynamically. e.g. a vector has capacity=0 in the beginning, with pushing back elements, it's capacity increases gradually to 1, 2, 4, 8, ...

Each time it increases the capacity, it has to copy/move old elements into the newly created array of memory.

My question is, when the class of element has both copy constructor and rvalue constructor (for move-semantics), the std::vector will prefer the copy constructor instead of rvalue constructor. Why is that? Since there's obviously nobody need the old variables.

Here's an example:

#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>

class foo {
public:
  foo()
    : id(-1)
    , c(nullptr)
  {
    std::cout << "foo()\n";
  }

  foo(int i)
    : id(i)
  {
    c = new char[100000000];
    std::cout << "foo(" << i << ")\n";
  }
//  foo(const foo& rhs)
//  {
//    if (rhs.c)
//    {
//    id = rhs.id;
//    std::cout << "copy " << id << std::endl;
//    c = new char[100000000];
//    std::memcpy(c, rhs.c, 100000000);
//    }
//  }
  foo(foo&& rhs)
  {
    id = rhs.id;
    std::cout << "move " << id << std::endl;
    c = rhs.c;
    rhs.c = nullptr;
  }
  ~foo()
  {
    if (c)
      {
        delete [] c;
        std::cout << "desctruct " << id << std::endl;
      }
  }
public:
  char* c;
  int id = -1;
};

std::vector<foo> getFoo()
{
  std::vector<foo> vec;
//  vec.reserve(3);
  for (int i = 0; i < 3; ++i)
    {
      foo f(i);
      vec.push_back(std::move(f));
      std::cout << "capacity: " << vec.capacity() << std::endl;
    }
  return vec;
}

int main(int argc, char *argv[])
{
  using namespace std::chrono;
  auto tic = high_resolution_clock::now();
  auto&& vec = getFoo();
  std::cout << "vec size: " << vec.size() << std::endl;
  auto toc = high_resolution_clock::now();
  std::cout << duration_cast<microseconds>(toc - tic).count() << "us" << std::endl;
  return 0;
}

As you can see, if I comment out the copy constructor, only move operation is involved:

foo(0)
move 0
capacity: 1
foo(1)
move 1
move 0
capacity: 2
foo(2)
move 2
move 1
move 0
capacity: 4
vec size: 3
165us
desctruct 2
desctruct 1
desctruct 0

However, if I uncomment the copy constructor, then there will be many copy operations, which is very expensive:

foo(0)
move 0
capacity: 1
foo(1)
move 1
copy 0
desctruct 0
capacity: 2
foo(2)
move 2
copy 1
copy 0
desctruct 1
desctruct 0
capacity: 4
vec size: 3
1008324us
desctruct 2
desctruct 1
desctruct 0

How can I avoid such copy operations?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
victl
  • 161
  • 1
  • 6
  • 5
    Possible duplicate of [How to enforce move semantics when a vector grows?](https://stackoverflow.com/questions/8001823/how-to-enforce-move-semantics-when-a-vector-grows) and https://stackoverflow.com/questions/10127603/why-does-reallocating-a-vector-copy-instead-of-moving-the-elements – stijn Jul 22 '17 at 08:29

0 Answers0