将vector附加到vector
假设我有2个标准向量:
vector<int> a; vector<int> b;
我们还要说,这两者都有大约30个元素。
- 如何将向量b添加到向量a的末尾?
肮脏的方式将通过b迭代,并通过vector<int>::push_back()
添加每个元素,但我不想这样做!
a.insert(a.end(), b.begin(), b.end());
要么
a.insert(std::end(a), std::begin(b), std::end(b));
第二个变种是一个更普遍适用的解决scheme,因为b
也可以是一个数组。 但是,它需要C ++ 11
std::copy (b.begin(), b.end(), std::back_inserter(a));
这可以用于向量a中的项没有赋值运算符(例如const成员)的情况。
在所有其他情况下,与上述插入解决scheme相比,此解决scheme是无效的。
在说“编译器可以预留”的时候,为什么要依靠呢? 那么移动语义的自动检测呢? 那么在begin
和end
的时候重复容器名称呢?
你不想要什么,你知道,更简单吗?
(向下滚动至main
)
#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); }
嘿嘿
现在,使用移动数据从rhs,append-array-to-container,追加forward_list-to-container,移动container-from-lhs,感谢@ DyP的帮助。
请注意,以上不能在铿锵编译感谢EnableFunctionIf<>...
技术。 在铛这个解决方法工作。
如果你想添加vector本身,两种stream行的解决scheme将失败:
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" }