Appending a vector to a vector
本问题已经有最佳答案,请猛点这里访问。
假设我有两个标准向量:
1 2 | vector<int> a; vector<int> b; |
假设两者都有大约30个元素。
- 如何将向量b添加到向量a的末尾?
最糟糕的方法是通过b迭代并通过
1 | a.insert(a.end(), b.begin(), b.end()); |
或
1 | a.insert(std::end(a), std::begin(b), std::end(b)); |
第二种变体是一种更通用的解决方案,因为
1 | std::copy (b.begin(), b.end(), std::back_inserter(a)); |
这可用于向量A中的项没有赋值运算符(例如const成员)的情况。
在所有其他情况下,与上述插入解决方案相比,此解决方案无效。
当说"编译器可以保留"时,为什么要依赖它?那么自动检测移动语义呢?那么,重复使用
你不想要更简单的东西吗?
(向下滚动到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | #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::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 <<" "; }; 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); } |
呵呵。
现在,在@dyp的帮助下,使用从rhs移动数据、将数组附加到容器、将列表附加到容器、将容器从lhs移动。
请注意,由于
如果您想将向量添加到自身,两种流行的解决方案都将失败:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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" } |