Concatenating two std::vectors

后端 未结 25 2320
予麋鹿
予麋鹿 2020-11-22 12:00

How do I concatenate two std::vectors?

相关标签:
25条回答
  • 2020-11-22 12:18

    You should use vector::insert

    v1.insert(v1.end(), v2.begin(), v2.end());
    
    0 讨论(0)
  • 2020-11-22 12:22

    I've implemented this function which concatenates any number of containers, moving from rvalue-references and copying otherwise

    namespace internal {
    
    // Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
    // appropriate
    template<typename Target, typename Head, typename... Tail>
    void AppendNoReserve(Target* target, Head&& head, Tail&&... tail) {
        // Currently, require each homogenous inputs. If there is demand, we could probably implement a
        // version that outputs a vector whose value_type is the common_type of all the containers
        // passed to it, and call it ConvertingConcatenate.
        static_assert(
                std::is_same_v<
                        typename std::decay_t<Target>::value_type,
                        typename std::decay_t<Head>::value_type>,
                "Concatenate requires each container passed to it to have the same value_type");
        if constexpr (std::is_lvalue_reference_v<Head>) {
            std::copy(head.begin(), head.end(), std::back_inserter(*target));
        } else {
            std::move(head.begin(), head.end(), std::back_inserter(*target));
        }
        if constexpr (sizeof...(Tail) > 0) {
            AppendNoReserve(target, std::forward<Tail>(tail)...);
        }
    }
    
    template<typename Head, typename... Tail>
    size_t TotalSize(const Head& head, const Tail&... tail) {
        if constexpr (sizeof...(Tail) > 0) {
            return head.size() + TotalSize(tail...);
        } else {
            return head.size();
        }
    }
    
    }  // namespace internal
    
    /// Concatenate the provided containers into a single vector. Moves from rvalue references, copies
    /// otherwise.
    template<typename Head, typename... Tail>
    auto Concatenate(Head&& head, Tail&&... tail) {
        size_t totalSize = internal::TotalSize(head, tail...);
        std::vector<typename std::decay_t<Head>::value_type> result;
        result.reserve(totalSize);
        internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);
        return result;
    }
    
    0 讨论(0)
  • 2020-11-22 12:24
    std::vector<int> first;
    std::vector<int> second;
    
    first.insert(first.end(), second.begin(), second.end());
    
    0 讨论(0)
  • 2020-11-22 12:24

    Here's a general purpose solution using C++11 move semantics:

    template <typename T>
    std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
    {
        if (lhs.empty()) return rhs;
        if (rhs.empty()) return lhs;
        std::vector<T> result {};
        result.reserve(lhs.size() + rhs.size());
        result.insert(result.cend(), lhs.cbegin(), lhs.cend());
        result.insert(result.cend(), rhs.cbegin(), rhs.cend());
        return result;
    }
    
    template <typename T>
    std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
    {
        lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
        return std::move(lhs);
    }
    
    template <typename T>
    std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
    {
        rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
        return std::move(rhs);
    }
    
    template <typename T>
    std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
    {
        if (lhs.empty()) return std::move(rhs);
        lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
        return std::move(lhs);
    }
    

    Note how this differs from appending to a vector.

    0 讨论(0)
  • 2020-11-22 12:26

    If what you're looking for is a way to append a vector to another after creation, vector::insert is your best bet, as has been answered several times, for example:

    vector<int> first = {13};
    const vector<int> second = {42};
    
    first.insert(first.end(), second.cbegin(), second.cend());
    

    Sadly there's no way to construct a const vector<int>, as above you must construct and then insert.


    If what you're actually looking for is a container to hold the concatenation of these two vector<int>s, there may be something better available to you, if:

    1. Your vector contains primitives
    2. Your contained primitives are of size 32-bit or smaller
    3. You want a const container

    If the above are all true, I'd suggest using the basic_string who's char_type matches the size of the primitive contained in your vector. You should include a static_assert in your code to validate these sizes stay consistent:

    static_assert(sizeof(char32_t) == sizeof(int));
    

    With this holding true you can just do:

    const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
    

    For more information on the differences between string and vector you can look here: https://stackoverflow.com/a/35558008/2642059

    For a live example of this code you can look here: http://ideone.com/7Iww3I

    0 讨论(0)
  • 2020-11-22 12:28

    If you are using C++11, and wish to move the elements rather than merely copying them, you can use std::move_iterator along with insert (or copy):

    #include <vector>
    #include <iostream>
    #include <iterator>
    
    int main(int argc, char** argv) {
      std::vector<int> dest{1,2,3,4,5};
      std::vector<int> src{6,7,8,9,10};
    
      // Move elements from src to dest.
      // src is left in undefined but safe-to-destruct state.
      dest.insert(
          dest.end(),
          std::make_move_iterator(src.begin()),
          std::make_move_iterator(src.end())
        );
    
      // Print out concatenated vector.
      std::copy(
          dest.begin(),
          dest.end(),
          std::ostream_iterator<int>(std::cout, "\n")
        );
    
      return 0;
    }
    

    This will not be more efficient for the example with ints, since moving them is no more efficient than copying them, but for a data structure with optimized moves, it can avoid copying unnecessary state:

    #include <vector>
    #include <iostream>
    #include <iterator>
    
    int main(int argc, char** argv) {
      std::vector<std::vector<int>> dest{{1,2,3,4,5}, {3,4}};
      std::vector<std::vector<int>> src{{6,7,8,9,10}};
    
      // Move elements from src to dest.
      // src is left in undefined but safe-to-destruct state.
      dest.insert(
          dest.end(),
          std::make_move_iterator(src.begin()),
          std::make_move_iterator(src.end())
        );
    
      return 0;
    }
    

    After the move, src's element is left in an undefined but safe-to-destruct state, and its former elements were transfered directly to dest's new element at the end.

    0 讨论(0)
提交回复
热议问题