836

Assuming I have 2 standard vectors:

vector<int> a;
vector<int> b;

Let's also say the both have around 30 elements.

  • How do I add the vector b to the end of vector a?

The dirty way would be iterating through b and adding each element via vector<int>::push_back(), though I wouldn't like to do that!

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
sub
  • 8,403
  • 3
  • 16
  • 3
  • 75
    I guess everyone will post answers using iterators. I've never worked out why vector doesn't have op+=() or an append() function. –  Mar 31 '10 at 09:36
  • 26
    @Neil Because `insert` is sufficient? – Andreas Brinck Mar 31 '10 at 09:40
  • 43
    @Andreas Well, couldn't the same be said for std::string? Of course insert() is sufficient, but its far from obvious in your answer that what is actually happening is one vector being appended to another. a += b makes this transparent. –  Mar 31 '10 at 09:43
  • 67
    @Andreas: It might be sufficient performance-wise, but it's not as easy to read. IMO `a.append(b)` (or even `a+=b`) would capture the intent much better than `a.insert(a.end(), b.begin(), b.end())`. – sbi Mar 31 '10 at 09:44
  • Maybe someone nice will add the append function to the vector template one day :) – sub Mar 31 '10 at 09:46
  • 3
    @Neil `std::string` is considered by many to be a spectacular design failure. There's no need to add an `append` function to `vector` if you really need it you can write your own version as a free function. – Andreas Brinck Mar 31 '10 at 09:50
  • 4
    @Neil Quoting Herb Sutter and Andrei Alexandrescu: "`std::basic_string` is an equally infamous example of monolithic class design..." – Andreas Brinck Mar 31 '10 at 09:53
  • 25
    @Andreas I take it you are referring to the "fat interface" issue. Some classes should have fat interfaces, and IMHO strings are one of them - I find std::string extemely usable, no matter what purists may say. I simply think that vector could do with putting on a little weight to make life easier for its users and clearer for readers of their code. –  Mar 31 '10 at 09:53
  • Same question for list: http://stackoverflow.com/questions/2349098/c-linked-list-behavior/2349119#2349119, and interestingly the same answers :-) – Arun Apr 01 '10 at 02:59
  • 4
    @Neil: I am with you on the `std::vector::append()` and I was with you on `+=` until I saw http://stackoverflow.com/questions/2552839/2553683#2553683. – sbi Apr 02 '10 at 16:03
  • 1
    The problem with `operator+()` in my opinion is that it implies (albeit subtly) an O(1) operation. Yes, strings have it, but strings also have the famous pitfall of abusing `operator+()`. It's a problem, and I like the approach C++ makes to make it harder for you to write inefficient code. – wilhelmtell Apr 21 '10 at 05:03
  • 3
    I feel like a lot of things in the C++ stdlib could use some TLC in terms of naming and convenience of use. I would be all for an append() function, even if it doesn't save much typing it's way clearer when reading the code. – Ibrahim Nov 02 '12 at 08:24
  • 1
    How about going Perly and override the `operator.` to append? – John May 30 '13 at 22:09
  • 4
    @John `operator.` cannot be overriden. – Yakk - Adam Nevraumont May 31 '13 at 15:03
  • 2
    @anon The design intent here is to be generic(a.k.a. data-type independent). If + supported then it would be likely be implemented using + defined for data-type. This would put a big restriction one data-types that can be used with vector. – qqqqq Jan 15 '16 at 17:53
  • 2
    @AndreasBrinck Assembly is "sufficient" for any programming task as well; it's also very fast. That doesn't mean that it is easy to use, or even readable. – ack Mar 18 '18 at 21:24
  • 5
    "everyone can just re-implement basic, commonly used functionality hundreds of times" - what a classic 'C++ guy' way of looking at usability. – mackenir Jun 26 '19 at 11:28
  • 2
    For those who skimmed over sbi's last comment above, `a+=b` could be interpreted as element-wise add-assignment, especially to those familiar with linear algebra libraries dealing with something called a `vector` (a one-dimensional matrix in math terms). – Emile Cormier Feb 15 '20 at 21:28
  • There's another issue (especially when it comes to code readability: if I'm trying to append a vector returned by some function to an existing vector, I'll have to add a line to save the vector before calling the insert. Probably the same performance-wise, but definitely less readable. – PixelArtDragon Jul 23 '20 at 19:51
  • https://cplusplus.com/reference/vector/vector/insert/ – KNU Apr 27 '23 at 09:58

4 Answers4

1468
a.insert(a.end(), b.begin(), b.end());

or

a.insert(std::end(a), std::begin(b), std::end(b));

The second variant is a more generically applicable solution, as b could also be an array. However, it requires C++11. If you want to work with user-defined types, use ADL:

using std::begin, std::end;
a.insert(end(a), begin(b), end(b));
L. F.
  • 19,445
  • 8
  • 48
  • 82
Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • 25
    Do I need to `reserve` before `insert`? – Violet Giraffe Aug 14 '13 at 06:50
  • 12
    @VioletGiraffe reserve is not required but may be advisable. It's smart to use reserve if you are repeatedly inserting into a vector for which you know the final size, and that size is large. Otherwise, I'd let the STL grow your vector as needed. – moodboom Sep 10 '13 at 16:24
  • 25
    This solution fails if you try to append vector to itself. It generates vector with correct size but adds empty elements instead of original. And it begins working only if you prepend it by v.reserve(v.size()*2); (but it may depend on STL implementation) – Sergey Nov 14 '13 at 17:16
  • 18
    @Sergey I believe that the standard specifically says that the iterators given to `insert` must not be from the same range as the receiver object's elements, so I suppose that technically speaking it's UB. – templatetypedef Jan 30 '14 at 21:47
  • @sergey worse than that -- the `end` iterator you pass for the end of the `vector` range is invalidated by the insert, even if you reserve first (as it is at, or after, the point of insertion, which invalidates it). Debugging mode `std::vector` may catch this (most release won't, and will just work). – Yakk - Adam Nevraumont Oct 30 '14 at 18:24
  • @templatetypedef do you have a citation for that? The workaround I'm aware of might still get into trouble here. – Yakk - Adam Nevraumont Oct 30 '14 at 18:24
  • 17
    @Yakk In my draft C++14 standard, Table 100 (Sequence Container Requirements) lists as a precondition of the call `a.insert(p, i, j)` that "i and j are not iterators into a." – templatetypedef Oct 30 '14 at 18:36
  • @moodboom you shell not use reserve before single insert if it is not the last insert. In most implementations vector::reserve do not grow the vector buffer exponentially, it sets the buffer size to precise amount of elements you reserved. So for example a series of calls "reserve(size() + 1)" has quadratic complexity – ZAB Mar 05 '15 at 16:10
  • @ZAB If you are saying multiple calls to reserve is questionable practice, I agree, and you should let the library grow the vector as needed. That's why I said to use it (once) if you know the desired final size. But is that what you are saying? :-) – moodboom Mar 05 '15 at 22:17
  • @Sergey, do not really know this abriviation `UB` - could you elaborate what : " it's UB." - means – serup Jan 24 '17 at 09:01
  • @serup 'UB' is referring to undefined behaviour. http://en.cppreference.com/w/cpp/language/ub – cgmb Mar 23 '17 at 22:47
101
std::copy (b.begin(), b.end(), std::back_inserter(a));

This can be used in case the items in vector a have no assignment operator (e.g. const member).

In all other cases this solution is ineffiecent compared to the above insert solution.

dandan78
  • 13,328
  • 13
  • 64
  • 78
  • 72
    Note that this is likely to be less efficient than using `std::vector<>::insert()`, because `std::copy()` can't reserve enough space before-hand (it doesn't have access to the vector itself, only to an iterator which has), while `std::vector<>::insert()`, being a member function, can. (It needs to figure out that the iterators to read from are random-access iterators in order to pre-calculate the sequence's length, but it would be a weak implementation which wouldn't do this.) – sbi Mar 31 '10 at 09:42
  • 3
    True in practice, but in theory the `std::` implementor can make it work. They can use non-standard extensions internally. – MSalters Mar 31 '10 at 10:38
  • 7
    @MSalter: I know that an implementation _could_ do this. This is why I wrote it is "likely to be less efficient". In theory, an implementer could add a private interface to `std::back_inserter_iterator>` to allow an implementation of `std::copy()` to recognize it and use this private interface to get hold of the `std::vector` and call `reserve()` on it. In practice, however, it's unlikely any implementer jumps through all these hoops to optimize such a corner case. – sbi Mar 31 '10 at 14:07
  • 5
    @sbi's criticism is correct, atleast for libstdc++. `std::copy` is indeed slower than using `std::vector::insert`. I just tested it with libstdc++ that came with g++ 4.4.5. – Marc Claesen May 30 '13 at 21:43
  • This solution generates std::bad_alloc exception if you try to append vector to itself. But it begins working if you prepend it by v.reserve(v.size()*2); (but it may depend on STL implementation) – Sergey Nov 14 '13 at 17:30
  • 1
    @Sergey, tring to append vector to itself is UB: http://stackoverflow.com/questions/14791984/appending-stdvector-to-itself-undefined-behavior –  Nov 14 '13 at 19:09
  • 1
    This also works, if the elements of the vector are constructable but not assignable. (e.g. they contain non-static ```const``` data members) – ManuelAtWork Sep 18 '17 at 08:50
47

While saying "the compiler can reserve", why rely on it? And what about automatic detection of move semantics? And what about all that repeating of the container name with the begins and ends?

Wouldn't you want something, you know, simpler?

(Scroll down to main for the punchline)

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

template<typename C,typename=void> struct can_reserve: std::false_type {};

template<typename T, typename A>
struct can_reserve<std::vector<T,A>,void>:
    std::true_type
{};

template<int n> struct secret_enum { enum class type {}; };
template<int n>
using SecretEnum = typename secret_enum<n>::type;

template<bool b, int override_num=1>
using EnableFuncIf = typename std::enable_if< b, SecretEnum<override_num> >::type;
template<bool b, int override_num=1>
using DisableFuncIf = EnableFuncIf< !b, -override_num >;

template<typename C, EnableFuncIf< can_reserve<C>::value >... >
void try_reserve( C& c, std::size_t n ) {
  c.reserve(n);
}
template<typename C, DisableFuncIf< can_reserve<C>::value >... >
void try_reserve( C& c, std::size_t ) { } // do nothing

template<typename C,typename=void>
struct has_size_method:std::false_type {};
template<typename C>
struct has_size_method<C, typename std::enable_if<std::is_same<
  decltype( std::declval<C>().size() ),
  decltype( std::declval<C>().size() )
>::value>::type>:std::true_type {};

namespace adl_aux {
  using std::begin; using std::end;
  template<typename C>
  auto adl_begin(C&&c)->decltype( begin(std::forward<C>(c)) );
  template<typename C>
  auto adl_end(C&&c)->decltype( end(std::forward<C>(c)) );
}
template<typename C>
struct iterable_traits {
    typedef decltype( adl_aux::adl_begin(std::declval<C&>()) ) iterator;
    typedef decltype( adl_aux::adl_begin(std::declval<C const&>()) ) const_iterator;
};
template<typename C> using Iterator = typename iterable_traits<C>::iterator;
template<typename C> using ConstIterator = typename iterable_traits<C>::const_iterator;
template<typename I> using IteratorCategory = typename std::iterator_traits<I>::iterator_category;

template<typename C, EnableFuncIf< has_size_method<C>::value, 1>... >
std::size_t size_at_least( C&& c ) {
    return c.size();
}

template<typename C, EnableFuncIf< !has_size_method<C>::value &&
  std::is_base_of< std::random_access_iterator_tag, IteratorCategory<Iterator<C>> >::value, 2>... >
std::size_t size_at_least( C&& c ) {
    using std::begin; using std::end;
  return end(c)-begin(c);
};
template<typename C, EnableFuncIf< !has_size_method<C>::value &&
  !std::is_base_of< std::random_access_iterator_tag, IteratorCategory<Iterator<C>> >::value, 3>... >
std::size_t size_at_least( C&& c ) {
  return 0;
};

template < typename It >
auto try_make_move_iterator(It i, std::true_type)
-> decltype(make_move_iterator(i))
{
    return make_move_iterator(i);
}
template < typename It >
It try_make_move_iterator(It i, ...)
{
    return i;
}


#include <iostream>
template<typename C1, typename C2>
C1&& append_containers( C1&& c1, C2&& c2 )
{
  using std::begin; using std::end;
  try_reserve( c1, size_at_least(c1) + size_at_least(c2) );

  using is_rvref = std::is_rvalue_reference<C2&&>;
  c1.insert( end(c1),
             try_make_move_iterator(begin(c2), is_rvref{}),
             try_make_move_iterator(end(c2), is_rvref{}) );

  return std::forward<C1>(c1);
}

struct append_infix_op {} append;
template<typename LHS>
struct append_on_right_op {
  LHS lhs;
  template<typename RHS>
  LHS&& operator=( RHS&& rhs ) {
    return append_containers( std::forward<LHS>(lhs), std::forward<RHS>(rhs) );
  }
};

template<typename LHS>
append_on_right_op<LHS> operator+( LHS&& lhs, append_infix_op ) {
  return { std::forward<LHS>(lhs) };
}
template<typename LHS,typename RHS>
typename std::remove_reference<LHS>::type operator+( append_on_right_op<LHS>&& lhs, RHS&& rhs ) {
  typename std::decay<LHS>::type retval = std::forward<LHS>(lhs.lhs);
  return append_containers( std::move(retval), std::forward<RHS>(rhs) );
}

template<typename C>
void print_container( C&& c ) {
  for( auto&& x:c )
    std::cout << x << ",";
  std::cout << "\n";
};

int main() {
  std::vector<int> a = {0,1,2};
  std::vector<int> b = {3,4,5};
  print_container(a);
  print_container(b);
  a +append= b;
  const int arr[] = {6,7,8};
  a +append= arr;
  print_container(a);
  print_container(b);
  std::vector<double> d = ( std::vector<double>{-3.14, -2, -1} +append= a );
  print_container(d);
  std::vector<double> c = std::move(d) +append+ a;
  print_container(c);
  print_container(d);
  std::vector<double> e = c +append+ std::move(a);
  print_container(e);
  print_container(a);
}

hehe.

Now with move-data-from-rhs, append-array-to-container, append forward_list-to-container, move-container-from-lhs, thanks to @DyP's help.

Note that the above does not compile in clang thanks to the EnableFunctionIf<>... technique. In clang this workaround works.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I think you can simplify this, e.g. [the `try_reserve` part](http://coliru.stacked-crooked.com/view?id=4ff26889e8134023a0f2d2dd6962b8c7-c59112800dca6ae4674520521ddfe536) – dyp May 30 '13 at 21:26
  • Where do you use `size_at_least`? (I can only see declaration/definition, but no call..) – dyp May 30 '13 at 21:30
  • Only added the ["insert-by-move"](http://coliru.stacked-crooked.com/view?id=924c7f07cebeea9d727f1df6746d435e-c59112800dca6ae4674520521ddfe536) – dyp May 30 '13 at 21:41
  • @dyp It is supposed to be in the reserve call, so we can append lists to vectors. On phone, not up to editing. Will fix later. – Yakk - Adam Nevraumont May 31 '13 at 01:22
  • @DyP I mean, I want it to support raw C arrays which have random access iterators but no `size`, and `forward_list`s (with no efficient way to produce a `size`) and ranges of forward iterators. Your insert-by-move fails to compile if the RHS is a `const` container. – Yakk - Adam Nevraumont May 31 '13 at 13:50
  • Did you check that it doesn't compile? When commenting out `try_reserve`, it works fine with a `const int arr[] = {6,7,8}; a +append= std::move(arr);` Of course, `size_at_least` has to be introduced to fix the `try_reserve` part. It would probably fail if the type is not movable (e.g. deleted move-assignment), though. [Fixing that is simple.](http://coliru.stacked-crooked.com/view?id=8238acced3f619878f38da1800ff8f24-bb452bed83d06e87c370fcabced7e96d) – dyp May 31 '13 at 14:09
  • @DyP ah, no I just assumed that a `make_move_iterator` wouldn't work on a `const_iterator`. Bah, my `EnableFunctionIf` trick fails to compile on clang (I am not sure if clang is right in this failure to compile either) – Yakk - Adam Nevraumont May 31 '13 at 14:36
  • 66
    How does anyone use this language – Rag Feb 03 '14 at 20:23
  • 41
    @BrainGordon You do know that the above post is pretty much a joke? C++ has a turing-complete compile time sublanguage, using it to its full potential does quite often create write-only code. The punch line of the joke is in `main`, where if you skip the code-salad above, is shockingly readable: the humor is that this is "simpler", which is far, far, far from the case. What that unreadable code-salad does is *adds a named operator to the language*: there is no support in C++ for named operators, so it does it via strange tricks. It is also poorly written: I have gotten better since. – Yakk - Adam Nevraumont Feb 03 '14 at 20:33
  • 4
    http://orig05.deviantart.net/07ee/f/2012/132/c/f/the_horror_you_gais_by_ask_salad_fingers-d4zj0jc.png – user2962533 Jan 18 '16 at 14:08
29

If you would like to add vector to itself both popular solutions will fail:

std::vector<std::string> v, orig;

orig.push_back("first");
orig.push_back("second");

// BAD:
v = orig;
v.insert(v.end(), v.begin(), v.end());
// Now v contains: { "first", "second", "", "" }

// BAD:
v = orig;
std::copy(v.begin(), v.end(), std::back_inserter(v));
// std::bad_alloc exception is generated

// GOOD, but I can't guarantee it will work with any STL:
v = orig;
v.reserve(v.size()*2);
v.insert(v.end(), v.begin(), v.end());
// Now v contains: { "first", "second", "first", "second" }

// GOOD, but I can't guarantee it will work with any STL:
v = orig;
v.reserve(v.size()*2);
std::copy(v.begin(), v.end(), std::back_inserter(v));
// Now v contains: { "first", "second", "first", "second" }

// GOOD (best):
v = orig;
v.insert(v.end(), orig.begin(), orig.end()); // note: we use different vectors here
// Now v contains: { "first", "second", "first", "second" }
Sergey
  • 1,552
  • 20
  • 18
  • 11
    Apart from the last one all your suggestions are wrong as stated in the other posts (`insert` must not get iterators into the container it operates on and `copy`'s iterators are invalidated by the insertion via `back_inserter`). The two you labeled as "good" seem to work because there is no reallocation (because of your `reserve` call). The last one is the way to go. Another option that would actually allow to avoid a second container would be to use resize instead of reserve and then copy the first half of the vector to the newly allocated elements. – Nobody moving away from SE Nov 26 '14 at 14:32