Supponendo che ho 2 vettori standard:
vector a; vector b;
Diciamo anche che entrambi hanno circa 30 elementi.
Il modo sporco sarebbe scorrere iterando b e aggiungendo ogni elemento tramite vector::push_back()
, anche se non mi piacerebbe farlo!
a.insert(a.end(), b.begin(), b.end());
o
a.insert(std::end(a), std::begin(b), std::end(b));
La seconda variante è una soluzione più genericamente applicabile, dato che b
potrebbe anche essere una matrice. Tuttavia, richiede C ++ 11
std::copy (b.begin(), b.end(), std::back_inserter(a));
Questo può essere usato nel caso in cui gli elementi nel vettore a non abbiano un operatore di assegnazione (ad esempio un membro const).
In tutti gli altri casi questa soluzione è inefficace rispetto alla soluzione inserto sopra.
Mentre si dice “il compilatore può prenotare”, perché contare su di esso? E per quanto riguarda il rilevamento automatico della semantica del movimento? E che dire di tutto quel ripetersi del nome del contenitore con le begin
s e le end
s?
Non vorresti qualcosa, lo sai, più semplice?
(Scorrere verso il basso fino alla linea main
per la battuta finale)
#include #include #include #include template struct can_reserve: std::false_type {}; template struct can_reserve,void>: std::true_type {}; template struct secret_enum { enum class type {}; }; template using SecretEnum = typename secret_enum::type; template using EnableFuncIf = typename std::enable_if< b, SecretEnum >::type; template using DisableFuncIf = EnableFuncIf< !b, -override_num >; template::value >... > void try_reserve( C& c, std::size_t n ) { c.reserve(n); } template ::value >... > void try_reserve( C& c, std::size_t ) { } // do nothing template struct has_size_method:std::false_type {}; template struct has_size_method().size() ), decltype( std::declval ().size() ) >::value>::type>:std::true_type {}; namespace adl_aux { using std::begin; using std::end; template auto adl_begin(C&&c)->decltype( begin(std::forward(c)) ); template auto adl_end(C&&c)->decltype( end(std::forward(c)) ); } template struct iterable_traits { typedef decltype( adl_aux::adl_begin(std::declval()) ) iterator; typedef decltype( adl_aux::adl_begin(std::declval()) ) const_iterator; }; template using Iterator = typename iterable_traits::iterator; template using ConstIterator = typename iterable_traits::const_iterator; template using IteratorCategory = typename std::iterator_traits::iterator_category; template::value, 1>... > std::size_t size_at_least( C&& c ) { return c.size(); } template ::value && std::is_base_of< std::random_access_iterator_tag, IteratorCategory> >::value, 2>... > std::size_t size_at_least( C&& c ) { using std::begin; using std::end; return end(c)-begin(c); }; template::value && !std::is_base_of< std::random_access_iterator_tag, IteratorCategory> >::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 template 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; c1.insert( end(c1), try_make_move_iterator(begin(c2), is_rvref{}), try_make_move_iterator(end(c2), is_rvref{}) ); return std::forward(c1); } struct append_infix_op {} append; template struct append_on_right_op { LHS lhs; template LHS&& operator=( RHS&& rhs ) { return append_containers( std::forward(lhs), std::forward(rhs) ); } }; template append_on_right_op operator+( LHS&& lhs, append_infix_op ) { return { std::forward (lhs) }; } template typename std::remove_reference::type operator+( append_on_right_op && lhs, RHS&& rhs ) { typename std::decay ::type retval = std::forward (lhs.lhs); return append_containers( std::move(retval), std::forward(rhs) ); } template void print_container( C&& c ) { for( auto&& x:c ) std::cout << x << ","; std::cout << "\n"; }; int main() { std::vector a = {0,1,2}; std::vector 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 d = ( std::vector {-3.14, -2, -1} +append= a ); print_container(d); std::vector c = std::move(d) +append+ a; print_container(c); print_container(d); std::vector e = c +append+ std::move(a); print_container(e); print_container(a); }
hehe .
Ora con move-data-from-rhs, append-array-to-container, aggiungi forward_list-to-container, move-container-from-lhs, grazie alla guida di @DyP.
Nota che quanto sopra non viene compilato in clang grazie alla tecnica EnableFunctionIf<>...
In clang questa soluzione funziona.
Se vuoi aggiungere un vettore a se stesso, entrambe le soluzioni popolari falliranno:
std::vector 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" }