Append a copy of std::vector to the end of itself

2019-07-01 23:24发布

问题:

This question already has an answer here:

  • Nice way to append a vector to itself 4 answers

I am trying to make a copy of a vector of string and append it to the end of its original vector, i.e. duplicating its contents. Example:

 Input : vector<string> s = {"abc", "def"}
 Output: vector<string> s = {"abc", "def", "abc", "def"}

I was using the insert method, i.e.

s.insert(s.end(), s.begin(), s.end());

However, this exhibits compiler-dependent results. In, LLVM clang, it gave me the expected answer.

With GCC it gave me

 Output: vector<string> s = {"abc", "def", "", ""}

I am wondering why this happens and what's the safest way to achieve this vector duplication goal?

Here is the ideone.com link for the program above: http://ideone.com/40CH8q

回答1:

Although it can possibly be done with iterators, a safe alternative is to avoid them:

size_t size = v.size();  // Of course we shouldn't access .size() in the loop...
v.reserve(size * 2);     // Preallocation. Thanks @Ali for this performance hint
for (size_t i = 0; i < size; ++i)
    v.push_back(v[i]);

In general, working with iterators while also modifying the data structure (not only its elements) is dangerous; you should read carefully when iterators are invalidated and when it's safe to reuse old iterators after a modification. Thus, it sometimes makes sense to use the "old" method to iterate through a random-access sequence: using an index variable.



回答2:

As others have already pointed out, you need to make sure by calling vector::reserve() that the vector doesn't get reallocated during insertion. (It is also a good idea to call reserve if you chose to put the elements with push_back() into your vector.)

Then there is still the iterator invalidation issue (as detailed under vector::insert()) but the code below, as far as I know, bypasses that:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

using namespace std;

int main() {

  vector<string> s{"abc", "def"};

  auto size = s.size();

  s.reserve(2*size); // <-- This one is essential to ensure
                     //     no reallocation during insertion

  const string* first = s.data(); // using pointer, instead of iterator

  copy(first, first+size, back_inserter(s));

  for (const auto& e : s)
    cout << e << '\t';

  cout << endl;
}


回答3:

As already stated, it seems all you need to do is make a call to reserve:

sv.reserve(sv.size() * 2);

This is because if a reallocation occurs, the iterators are invalidated. You can check this for yourself:

std::cout << sv.capacity() << "\n"; // 2

The new size will be bigger than the capacity so a reallocation occurs.



回答4:

This can be done this way too

vector<string> data = {"abc", "def"}; // Input String Vector
int len = data.size();
for(int i = 0; i < len; i++)
{
    data.push_back(data[i]); // Making Copy of String Vector
}

vector<string> data = {"abc", "def", "abc", "def"};
//Resultant String Vector