Question Ajouter un vecteur à un vecteur [dupliquer]


Cette question a déjà une réponse ici:

En supposant que j'ai 2 vecteurs standards:

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

Disons aussi que les deux ont environ 30 éléments.

  • Comment ajouter le vecteur b à la fin du vecteur a?

Le chemin sale serait itération à travers b et en ajoutant chaque élément via vector<int>::push_back(), bien que je n'aimerais pas faire ça!


501
2018-03-31 09:33


origine


Réponses:


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

ou

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

La deuxième variante est une solution plus génériquement applicable, b pourrait aussi être un tableau. Cependant, il nécessite C ++ 11


936
2018-03-31 09:34



std::copy (b.begin(), b.end(), std::back_inserter(a));

Cela peut être utilisé dans le cas où les éléments dans le vecteur a n'ont pas d'opérateur d'affectation (par exemple, membre const).

Dans tous les autres cas, cette solution n'est pas efficace par rapport à la solution d'insertion ci-dessus.


61
2018-05-30 21:15



En disant "le compilateur peut réserver", pourquoi vous y fier? Et qu'en est-il de la détection automatique de la sémantique de mouvement? Et que dire de tout ce qui se répète du nom du conteneur avec le beginle sable ends?

Tu ne voudrais pas quelque chose, tu sais, plus simple?

(Faites défiler jusqu'à main pour la 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.

Maintenant avec move-data-from-rhs, ajoute-array-to-container, ajoute forward_list-to-container, déplace-container-from-lhs, grâce à l'aide de @ DyP.

Notez que ce qui précède ne compile pas en clang grâce à la EnableFunctionIf<>... technique. Dans le bruit cette solution de contournement travaux.


33
2017-11-14 17:34



Si vous souhaitez ajouter le vecteur à lui-même, les deux solutions populaires échoueront:

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" }

25