174

I am trying to efficiently make a copy of a vector. I see two possible approaches:

std::vector<int> copyVecFast1(const std::vector<int>& original)
{
  std::vector<int> newVec;
  newVec.reserve(original.size());
  std::copy(original.begin(), original.end(), std::back_inserter(newVec));
  return newVec;
}

std::vector<int> copyVecFast2(std::vector<int>& original)
{
  std::vector<int> newVec;
  newVec.swap(original);
  return newVec;
}

Which of these is preferred, and why? I am looking for the most efficient solution that will avoid unnecessary copying.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574

6 Answers6

278

They aren't the same though, are they? One is a copy, the other is a swap. Hence the function names.

My favourite is:

a = b;

Where a and b are vectors.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 3
    In fact the approach is passing by value, the compiler calls the copy constructor, and then swapping that newly created element. That is why rlbond suggests calling the copy constructor directly to achieve the same effect. – David Rodríguez - dribeas Mar 14 '09 at 00:17
  • 1
    However, you can't call rlbon without a function that passes the original as val. Otherwise, the original one will be empties. The second solution made sure that you will always call by value and hence you will not lose the date in the original vector. (Assuming swap deals with pointers) – Eyad Ebrahim Mar 31 '13 at 13:57
  • Won't that move the elements of b to a (leaving b with size == 0)? – Jonathan. Oct 23 '14 at 16:00
  • 1
    @Jonathan. Assuming you're talking about `a = b` then no. Assignment means: make `a` equal `b` without changing `b`. By contrast, `std::swap(a, b)` would exchange their contents (so `b`'s `size` would now be whatever `a`'s had been before). You are perhaps thinking of a move operation (as occurs in C++11, but not in an ordinary assignment like this). Such a move would leave `b` in an, ahem, "interesting" state - see http://stackoverflow.com/questions/17730689/is-a-moved-from-vector-always-empty – Daniel Earwicker Oct 24 '14 at 11:39
  • Note that there is an old C++ standard library class called `auto_ptr` which implemented assignment wrongly so that it performed a move. – Daniel Earwicker Oct 24 '14 at 11:42
  • @Daniel, this page says that in C++11 if the vector isn't const then the = operator is a move? – Jonathan. Oct 24 '14 at 12:31
  • @Jonathan. Which page says that? – Daniel Earwicker Oct 24 '14 at 12:47
  • @Daniel, sorry this page (http://www.cplusplus.com/reference/vector/vector/operator=/) when you click on the C++ tabs – Jonathan. Oct 24 '14 at 13:42
  • 1
    @Jonathan. Note the double ampersand `&&`. That version will only be used for an rvalue reference. It won't match any non-const value (such as `b` in my example above). You can turn `b` into one by saying `a = std::move(b);` See http://en.cppreference.com/w/cpp/language/value_category for even greater levels of complexity. – Daniel Earwicker Oct 24 '14 at 15:29
  • This is also my favorite. I traced into it, and it created exact copy of member fields `unsigned char pubkey[32];` in my object. But of course, `&pubkey` have different addresses. But the byte array contents are equal. If your object has member fields like `unsigned char* pbuf;` then the address value of `pbuf` would match in the two objects. Meaning, that doing `pbuf[0] = 0x42;` inside object1 , produces the same change in object2 for this member field. – daparic Jul 24 '20 at 13:36
135

Your second example does not work if you send the argument by reference. Did you mean

void copyVecFast(vec<int> original) // no reference
{

  vector<int> new_;
  new_.swap(original); 
}

That would work, but an easier way is

vector<int> new_(original);
rlbond
  • 65,341
  • 56
  • 178
  • 228
88

This is another valid way to make a copy of a vector, just use its constructor:

std::vector<int> newvector(oldvector);

This is even simpler than using std::copy to walk the entire vector from start to finish to std::back_insert them into the new vector.

That being said, your .swap() one is not a copy, instead it swaps the two vectors. You would modify the original to not contain anything anymore! Which is not a copy.

phoenix
  • 7,988
  • 6
  • 39
  • 45
X-Istence
  • 16,324
  • 6
  • 57
  • 74
  • 1
    More flexible for me is `a = b;` because I already have member field `a` and I just need to assign it with the new value from `b` – daparic Jul 24 '20 at 13:22
32

Direct answer:

  • Use a = operator

We can use the public member function std::vector::operator= of the container std::vector for assigning values from a vector to another.

  • Use a constructor function

Besides, a constructor function also makes sense. A constructor function with another vector as parameter(e.g. x) constructs a container with a copy of each of the elements in x , in the same order.

Caution:

  • Do not use std::vector::swap

std::vector::swap is not copying a vector to another, it is actually swapping elements of two vectors, just as its name suggests. In other words, the source vector to copy from is modified after std::vector::swap is called, which is probably not what you are expected.

  • Deep or shallow copy?

If the elements in the source vector are pointers to other data, then a deep copy is wanted sometimes.

According to wikipedia:

A deep copy, meaning that fields are dereferenced: rather than references to objects being copied, new copy objects are created for any referenced objects, and references to these placed in B.

Actually, there is no currently a built-in way in C++ to do a deep copy. All of the ways mentioned above are shallow. If a deep copy is necessary, you can traverse a vector and make copy of the references manually. Alternatively, an iterator can be considered for traversing. Discussion on iterator is beyond this question.

References

The page of std::vector on cplusplus.com

Jerry Yang
  • 509
  • 4
  • 11
23
new_vector.assign(old_vector.begin(),old_vector.end()); // Method 1
new_vector = old_vector; // Method 2
Farid Chowdhury
  • 2,766
  • 1
  • 26
  • 21
14

you should not use swap to copy vectors, it would change the "original" vector.

pass the original as a parameter to the new instead.

Raz
  • 1,932
  • 2
  • 18
  • 23