68

I have two STL vectors A and B and I'd like to clear all elements of A and move all elements of B to A and then clear out B. Simply put, I want to do this:

std::vector<MyClass> A;
std::vector<MyClass> B;
....
A = B;
B.clear();

Since B could be pretty long, it takes k*O(N) to do this operation, where k is a constant, and N is max(size_of(A), size_of(B)). I was wondering if there could be a more efficient way to do so. One thing that I could think of is to define A and B as pointers and then copy pointers in constant time and clear out B.

aminfar
  • 2,297
  • 3
  • 27
  • 37

8 Answers8

130

Using C++11, it's as simple as:

A = std::move(B);

Now A contains the elements that were previously held by B, and B is now empty. This avoids copying: the internal representation is simply moved from B to A, so this is an O(1) solution.

As for C++03, as Prætorian states, you could swap the vectors. There is a specialization of the std::swap function, which takes std::vectors as its arguments. This effectively swaps the internal representation, so you end up avoiding creating copies of the elements held by them. This function works in O(1) complexity as well.

mfontanini
  • 21,410
  • 4
  • 65
  • 73
  • 1
    After you do this, can you call `B.clear()` and then continue using B as an empty vector, or is any use of `B` now undefined? – pavon Apr 20 '16 at 20:20
  • 3
    `B` will already be empty at that point, so `B.clear()` is a no-op. And yes, you can still use the vector, it's just an empty vector. – mfontanini May 02 '16 at 22:26
  • But why does [standard mention](http://en.cppreference.com/w/cpp/container/vector/operator%3D) that std::vector::operator=(vector&&) is linear-in-size? – suhdonghwi Mar 31 '17 at 18:48
  • 1
    It's linear in size of `*this` as all existing elements in the left hand side vector have to be destroyed before you can actually move the other vector's data into it. – mfontanini Apr 14 '17 at 16:44
  • 32
    FYI, B is *not empty*, it is in a **valid but unspecified state**. The size of `B` is not guaranteed to be `0` after the move, according to the standard. This is why `swap()` is still useful and move is not a replacement for it: It guarantees that as long as `A` was empty, `B` will now be empty as well. – void.pointer Sep 19 '17 at 14:16
  • 3
    @void.pointer cppreference's documentation does contradict you in this case. std::vector's move constructor definition includes: "Constructs the container with the contents of other using move semantics. Allocator is obtained by move-construction from the allocator belonging to other. After the move, other is guaranteed to be empty()" https://en.cppreference.com/w/cpp/container/vector/vector – sheeldotme Sep 23 '18 at 06:30
  • 8
    @steeldotme You're wrong. You've linked the move constructor documentation. This answer is using **move assignment**. – void.pointer Sep 23 '18 at 13:58
  • Does the standard make any guarantees about whether pointers or references to the moved-from vector's elements will still point to the corresponding element in the moved-to vector? – sleep Feb 23 '22 at 00:35
  • What about the data held by `A`? Is the memory allocation freed? – Hermis14 Mar 28 '23 at 14:00
  • @Hermis14 As it says here (https://en.cppreference.com/w/cpp/container/vector/operator%3D): all elements originally belonging to *this are either destroyed or replaced by element-wise move-assignment. Generally, one expects this in move semantics. The older resource is expected to be freed and the new resource is moved into its place. – Hari Apr 09 '23 at 20:00
20

If you have a C++11 compiler you can move B into A.

A = std::move(B);

If you're working with an older compiler, just swap the two

A.swap(B);

In both cases, the only O(N) operation will be clearing the contents of A. In the first case the clearing will be done during the assignment itself, while in the second it will happen when B goes out of scope (since the contents were swapped).

Praetorian
  • 106,671
  • 19
  • 240
  • 328
6

std::move works fine. Here is the sample code for the same

    vector<int> v1 = {1,2,3,10,20,30,100,200,300,999};
    vector<int> v2;

    cout << "Size of v1 before move = " << v1.size() << endl;
    cout << "Capacity of v1 before move = " << v1.capacity() << endl;

    v2 = std::move(v1);

    cout << "Size of v2 after move = " << v2.size() << endl;
    cout << "Capacity of v2 after move = " << v2.capacity() << endl;

    cout << "Size of v1 after move = " << v1.size() << endl;
    cout << "Capacity of v1 after move = " << v1.capacity() << endl;

-----------Output-------------------------
Size of v1 before move = 10
Capacity of v1 before move = 10
Size of v2 after move = 10
Capacity of v2 after move = 10
Size of v1 after move = 0
Capacity of v1 after move = 0
Sameer Ahuja
  • 71
  • 1
  • 7
5

I have two STL vectors A and B and I'd like to clear all elements of A and move all elements of B to A and then clear out B.

This can be done with a combination of swap. First swap A and B for the first half. Then swap an empty std::vector<> with B or call clear(). The difference is that clear() will not release the memory, but only destroy the objects:

std::vector<int> a, b; // initialize them somehow
swap(a,b);

// clear b without releasing the memory:
std::size_t capacity = b.capacity();
b.clear();
assert(b.capacity()==capacity);

// or release the memory
std::vector<int>().swap(b);
assert(b.capacity()==0);
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
4

just call clear on vector will take o(1) time, since clear will do nothing, If you really want to clear B after assign it to A, you could do the following

A.swap(B);
{
    std::Vector<..> C;
    c.swap(B);
}
mogulkahn
  • 79
  • 1
  • 5
2

The swap function does this.

#include <iostream>
#include <iterator>
#include <vector>

int main(int argc, char* argv)
{
  std::vector<int> A;
  std::vector<int> B;

  for (int i = 0; i < 10; ++i)
  {
     B.push_back(i);
  }

  std::cout << "Before swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";

  A.swap(B);
  B.clear();

  std::cout << "After swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";
}

The output

Before swap
A:
B:0 1 2 3 4 5 6 7 8 9 
After swap
A:0 1 2 3 4 5 6 7 8 9 
B:
Jason
  • 1,612
  • 16
  • 23
  • You cann't use `clear`. Functon `clear` just remove the element, but the memory allocated for this vector is not freed. – BlackMamba Sep 04 '13 at 05:46
2

If you can't std::move or std::swap the vectors (e.g., because A and B are related but different types, perhaps differing only by const), you can do:

std::vector<MyClass>       A;
std::vector<const MyClass> B;
// ...
for( auto& a : A )
{
    B.emplace_back( std::move( a ) );
}

Note that this leaves A with the same number of elements, but they are all in an indeterminate state (i.e., they can be assigned to or destructed, but not read).

metal
  • 6,202
  • 1
  • 34
  • 49
2

I lack rep to comment, but I want to mention that per: https://en.cppreference.com/w/cpp/container/vector/operator%3D void.pointer is right. In particular...

2) Move assignment operator. Replaces the contents with those of other using move semantics (i.e. the data in other is moved from other into this container). other is in a valid but unspecified state afterwards.

Thus Praetorian's answer is wrong per standard. However, for MSVC at least, is good enough because the implementation clears the list anyway (Probably true for most).

Something interesting is that since we declare a move constructor, no implicit move assignment operator will be declared. Thus we "know" that std::vector must declare a move assignment operator.