C++ 将向量附加到向量上

C++ 将向量附加到向量上,c++,stl,vector,C++,Stl,Vector,假设我有两个标准向量: vector<int> a; vector<int> b; 向量a; 载体b; 也就是说两者都有大约30个元素 如何将向量b添加到向量a的末尾 肮脏的方法是通过b进行迭代,并通过vector::push_back()添加每个元素,尽管我不想这样做 a.insert(a.end(), b.begin(), b.end()); 或 第二种变体是更通用的解决方案,因为b也可以是数组。但是,它需要C++11。如果要使用用户定义的类型,请使用ADL

假设我有两个标准向量:

vector<int> a;
vector<int> b;
向量a;
载体b;
也就是说两者都有大约30个元素

  • 如何将向量b添加到向量a的末尾
肮脏的方法是通过b进行迭代,并通过
vector::push_back()
添加每个元素,尽管我不想这样做

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

第二种变体是更通用的解决方案,因为
b
也可以是数组。但是,它需要C++11。如果要使用用户定义的类型,请使用ADL:

using std::begin, std::end;
a.insert(end(a), begin(b), end(b));
如果向量a中的项目没有赋值运算符(例如常量成员),则可以使用此选项

在所有其他情况下,与上述插入解决方案相比,此解决方案是不必要的。

当说“编译器可以保留”时,为什么要依赖它?那么移动语义的自动检测呢?那个么容器名称重复使用
begin
s和
end
s呢

难道你不想要更简单的东西吗

(向下滚动至
main
,查看punchline)

#包括
#包括
#包括
#包括
模板结构can_reserve:std::false_type{};
模板
结构can\U保留:
std::真_类型
{};
模板结构secret_enum{enum类类型{};};
模板
使用secretnum=typename secret\u enum::type;
模板
使用EnableFuncIf=typename std::enable_if::type;
模板
使用DisableFuncIf=EnableFuncIf<!b、 -覆盖_num>;
模板…>
无效试用保留(C&C,标准::尺寸){
c、 储备(n);
}
模板…>
void try\u reserve(C&C,std::size\u t){}//什么也不做
模板
结构具有_size_方法:std::false_type{};
模板
结构具有_size_method::type>:std::true_type{};
名称空间adl_aux{
使用std::begin;使用std::end;
模板
自动adl_开始(C&&C)->decltype(开始(std::forward(C));
模板
自动adl_end(C&&C)->decltype(end(std::forward(C));
}
模板
可结构的_特征{
typedef decltype(adl_aux::adl_begin(std::declval())迭代器;
typedef decltype(adl_aux::adl_begin(std::declval())常量迭代器;
};
使用Iterator=typename iterable_traits::Iterator的模板;
模板使用consterator=typename iterable\u traits::const\u迭代器;
使用IteratorCategory=typename std::iterator\u traits::iterator\u category的模板;
模板…>
标准::尺寸至少为尺寸(C&C){
返回c.size();
}
模板::值,2>…>
标准::尺寸至少为尺寸(C&C){
使用std::begin;使用std::end;
返回端(c)-开始(c);
};
模板::值,3>…>
标准::尺寸至少为尺寸(C&C){
返回0;
};
模板
自动尝试\u生成\u移动\u迭代器(It i,std::true\u类型)
->decltype(make_move_iterator(i))
{
返回make_move_迭代器(i);
}
模板
它尝试生成移动迭代器(It i,…)
{
返回i;
}
#包括
模板
C1&&append_容器(C1&&C1、C2&&C2)
{
使用std::begin;使用std::end;
尝试保留(c1,大小至少(c1)+大小至少(c2));
使用is_rvref=std::is_rvalue_引用;
c1.插入(端部(c1),
尝试生成移动迭代器(begin(c2),is_rvref{}),
try_make_move_迭代器(end(c2),is_rvref{});
返回std::向前(c1);
}
结构append_infix_op{}append;
模板
结构在右上追加{
LHS-LHS;
模板
左侧和右侧运算符=(右侧和右侧){
返回附加容器(std::forward(lhs)、std::forward(rhs));
}
};
模板
在右运算符+上追加(左和左,追加中缀运算){
返回{std::forward(lhs)};
}
模板
typename std::remove_reference::type operator+(在右上追加_\u op&&lhs、RHS&&RHS){
typename std::decage::type retval=std::forward(lhs.lhs);
返回附加容器(std::move(retval),std::forward(rhs));
}
模板
无效打印容器(C&C){
用于(自动和x:c)

std::cout如果要将向量添加到自身,两种常用解决方案都将失败:

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" }
std::向量v,原点;
初始推回(“第一”);
初始推回(“第二”);
//坏的:
v=原始值;
v、 插入(v.end(),v.begin(),v.end());
//现在v包含:{“第一”,“第二”,“第三”,“第三”}
//坏的:
v=原始值;
std::copy(v.begin()、v.end()、std::back_inserter(v));
//std::生成了错误的\u alloc异常
//很好,但我不能保证它能与任何STL一起工作:
v=原始值;
v、 储备(v.大小()*2);
v、 插入(v.end(),v.begin(),v.end());
//现在v包含:{“第一”、“第二”、“第一”、“第二”}
//很好,但我不能保证它能与任何STL一起工作:
v=原始值;
v、 储备(v.大小()*2);
std::copy(v.begin()、v.end()、std::back_inserter(v));
//现在v包含:{“第一”、“第二”、“第一”、“第二”}
//好(最好):
v=原始值;
v、 insert(v.end(),orig.begin(),orig.end());//注意:这里使用不同的向量
//现在v包含:{“第一”、“第二”、“第一”、“第二”}

我想每个人都会使用迭代器发布答案。我从来没有弄明白为什么vector没有op+=()或append()函数。@Neil因为
插入
就足够了?@Andreas,std::string不能这样说吗?当然插入()足够了,但在您的回答中,实际发生的是一个向量被附加到另一个向量上,这一点并不明显。a+=b使这一点变得透明。@Andreas:从性能角度看,这可能足够了,但它不那么容易阅读。IMO
a.append(b)
(甚至
a+=b
)比a.insert(a.end(),b.begin(),b.end())
@Andreas我想你指的是“fat接口”问题。有些类应该有胖接口,IMHO字符串就是其中之一——我发现std::string可以在外部使用,不管纯粹主义者怎么说。我只是认为vector可以增加一点权重,让用户的生活更轻松,让代码的读者更清楚。请注意,这可能比使用
std::vector::insert()
,因为
std::copy()
无法在手前保留足够的空间(它没有访问权限
std::copy (b.begin(), b.end(), std::back_inserter(a));
#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);
}
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" }