0

Which is the smartest way to add at the beggining of a vector the last two elements of the vector itself and add at the end of the vetor the first two element of the vector? I mean, if my starting vector is

v = 1 2 3 4 5 6 7 8 9

I need that it becames

v = 8 9 1 2 3 4 5 6 7 8 9 1 2
Wellen
  • 51
  • 7
  • 1
    Is `vector::insert()` not sufficient for you? – Captain Obvlious Jan 30 '14 at 23:34
  • Do you want to want to achieve something like periodic boundaries? If you are, you'd better go with applying the modulus operator on the index. – Markus Jan 30 '14 at 23:49
  • @Markus yes, but than i need to MPI_Send this vector and i think this is the easier way to continue my code – Wellen Jan 30 '14 at 23:52
  • I've no idea what MPI_Send is, but you might end in a pitfall, because modifying one value will not synchronize the other values. – Markus Jan 30 '14 at 23:57
  • @Markus MPI is used for parallel computing, however I don't need to modify the values of the vector – Wellen Jan 30 '14 at 23:59
  • 1
    Just keep in mind that these entries stay completely independent. – Markus Jan 31 '14 at 00:03
  • Don't worry! Insted are you shure that your answer is correct? I mean, there was another answer before in which it was said that I need to use reserve before to avoid a problem... – Wellen Jan 31 '14 at 00:06

3 Answers3

5

First, if the container is going to be large then consider using deque instead of vector. It's more efficient for adding at the start.

For vector you can't insert elements out of the vector to the start because the first thing that happens is everything in the vector gets moved (and all iterators and references to those elements are invalidated). So you either need to copy the elements out of the vector or you need to put insert elements at the start and then copy-assign to them. Assuming the type is int, I'll go with the former:

if (v.size() >= 2) {
    int tmp[] = {*(v.end() - 2), *(v.end() - 1)};
    v.insert(v.begin(), tmp, tmp + 2);
    tmp[0] = v[2]; tmp[1] = v[3];
    v.insert(v.end(), tmp, tmp + 2);
}

Alternatively, this uses more memory but might be easier to read. As a bonus it gives the strong exception guarantee even with types whose copy constructor can throw. My code above can be made to offer the strong guarantee by adding a call to reserve, but only because int is a trivial type:

if (v.size() >= 2) {
    std::vector<int> new_v;
    new_v.reserve(v.size() + 4);
    new_v.insert(new_v.end(), v.end() - 2, v.end());
    new_v.insert(new_v.end(), v.begin(), v.end());
    new_v.insert(new_v.end(), v.begin(), v.begin() + 2);
    v.swap(new_v);
}

For deque you don't need to store any elements outside the container provided you use a reference instead of an iterator to access them. Again this only offers the basic exception guarantee.

if (v.size() >= 2) {
    v.push_front(v.back());
    v.push_front(*&(v.end() - 1));
    v.push_back(*&(v.begin() + 2));
    v.push_back(*&(v.begin() + 3));
}
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Can you safely insert a range of iterators from a vector into itself? I thought that was UB. – templatetypedef Jan 30 '14 at 23:42
  • @SteveJessop your code doesn't work as I want, the output is `6 7 1 2 3 4 5 6 7 8 9 1 2` without the added line in the `vector` part works. I necessary need that line? – Wellen Jan 30 '14 at 23:46
  • @SteveJessop thank you for your help, however I need to use vector and your code dosen't works with the `reserve` line – Wellen Jan 30 '14 at 23:54
  • @SteveJessop Now it works, is this the easier way? Can you explain me why Marcus's answer is wrong? – Wellen Jan 31 '14 at 00:10
  • @SteveJessop: I think even at the end it's UB, because if the vector needs to resize, those iterators would be invalidated. – Mooing Duck Jan 31 '14 at 00:12
  • @Wellen: Markus's answer is wrong in two ways. The first way (which I got wrong too in the first version of my answer) is that calling `insert` might reallocate the vector, and if it does it invalidates all iterators. So by the time it tries to copy from them they may not longer be usable because the vector has moved to a different location. The second wrong thing (that I managed to get right), is that his second lines copies the last two elements of the vector to the start -- but those last two elements are the two that were copied from the start, not the original last two. – Steve Jessop Jan 31 '14 at 00:13
  • @MooingDuck: true, but one of the incorrect versions of my answer had a `reserve()` in it. Since it's no longer needed I've now removed it... – Steve Jessop Jan 31 '14 at 00:14
  • @Wellen: oh, and a further mistake that Markus and I both made, is that even if the vector doesn't reallocate it still invalidates all iterators and references after the insertion point. The `reserve()` I had prevents reallocation but of course doesn't prevent this problem. So you can't insert elements from the end at the beginning no matter what. – Steve Jessop Jan 31 '14 at 00:28
  • Tbh it's a miracle I got out of the mess I put myself in, without any downvotes. – Steve Jessop Jan 31 '14 at 00:37
  • Any insertion, anywhere, invalidates the `end()` iterator. Even if no reallocation is needed. Is there an `insert_n` like `copy_n`? If not, perhaps use `copy_n` with `back_inserter`. – Ben Voigt Jan 31 '14 at 00:47
  • @BenVoigt: I thought about `back_inserter`, decided that it didn't help the readability of any of my insertions to turn them into `copy`. But there's not a huge amount in it. – Steve Jessop Jan 31 '14 at 00:55
2

My five cents

    std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    v.insert( v.end(), { v[0], v[1], v[v.size() - 2], v[v.size() - 1] } );
    std::rotate( v.begin(), std::prev( v.end(), 2 ), v.end() );

    for ( int x : v ) std::cout << x << ' ';
    std::cout << std::endl;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

By using the vector::insert function for ranges. It is easier to first insert the first two elements at the end.

v.insert(v.end(), v.begin(), v.begin()+2);
v.insert(v.begin(), v.end()-2, v.end());

Edit: Is undefined behaviour.

Appending std::vector to itself, undefined behavior?

Community
  • 1
  • 1
Markus
  • 592
  • 3
  • 13
  • 2
    undefined behavior, the iterators may be invalidated before they're read from – Mooing Duck Jan 31 '14 at 00:13
  • I actually thought of that and therfore read the vector::insert reference at cplusplus.com. I did'nt find a hint, that it is UB. But you are acutually right it is Undefined Behaviour! [Discussed here](http://stackoverflow.com/a/14792174/2056153) If the source iterators belong to the target container, you are breaking a precondition. – Markus Jan 31 '14 at 13:59