Initialize std::array with a range (pair of iterators)

后端 未结 3 766
北恋
北恋 2020-12-05 14:16

How can I initialize an std::array from a range (as defined by a pair of iterators)?

Something like this:

vector v;
...
// I kn         


        
相关标签:
3条回答
  • 2020-12-05 14:51

    You can use BOOST_PP_ENUM as:

    include <boost/preprocessor/repetition/enum.hpp>
    
    #define INIT(z, i, v) v[i] 
    
    std::vector<int> v;
    
    //fill v with at least 5 items 
    
    std::array<int,5> a = { BOOST_PP_ENUM(5, INIT, v) };  //MAGIC
    

    Here, the last line is expanded as:

    std::array<int,5> a = {v[0], v[1], v[2], v[3], v[4]}; //EXPANDED LINE
    

    which is what you want.

    0 讨论(0)
  • 2020-12-05 15:07

    With random access iterators, and assuming a certain size at compile-time, you can use a pack of indices to do so:

    template <std::size_t... Indices>
    struct indices {
        using next = indices<Indices..., sizeof...(Indices)>;
    };
    template <std::size_t N>
    struct build_indices {
        using type = typename build_indices<N-1>::type::next;
    };
    template <>
    struct build_indices<0> {
        using type = indices<>;
    };
    template <std::size_t N>
    using BuildIndices = typename build_indices<N>::type;
    
    template <typename Iterator>
    using ValueType = typename std::iterator_traits<Iterator>::value_type;
    
    // internal overload with indices tag
    template <std::size_t... I, typename RandomAccessIterator,
              typename Array = std::array<ValueType<RandomAccessIterator>, sizeof...(I)>>
    Array make_array(RandomAccessIterator first, indices<I...>) {
        return Array { { first[I]... } };
    }
    
    // externally visible interface
    template <std::size_t N, typename RandomAccessIterator>
    std::array<ValueType<RandomAccessIterator>, N>
    make_array(RandomAccessIterator first, RandomAccessIterator last) {
        // last is not relevant if we're assuming the size is N
        // I'll assert it is correct anyway
        assert(last - first == N); 
        return make_array(first, BuildIndices<N> {});
    }
    
    // usage
    auto a = make_array<N>(v.begin(), v.end());
    

    This assumes a compiler capable of eliding the intermediate copies. I think that assumption is not a big stretch.

    Actually, it can be done with input iterators as well, since the computation of each element in a braced-init-list is sequenced before the computation of the next element (§8.5.4/4).

    // internal overload with indices tag
    template <std::size_t... I, typename InputIterator,
              typename Array = std::array<ValueType<InputIterator>, sizeof...(I)>>
    Array make_array(InputIterator first, indices<I...>) {
        return Array { { (void(I), *first++)... } };
    }    
    

    Since *first++ doesn't have any I in it, we need a dummy I to provoke the pack expansion. Comma operator to the rescue, with void() to silence warnings about lack of effects, and also preventing overloaded commas.

    0 讨论(0)
  • 2020-12-05 15:08

    Like you have noticed, std::array has no constructors at all (except for the compiler generated default constructor).

    This was done on purpose, so it can be statically initialized just like a C array. If you want to fill the array without a static initializer, you will have to copy your data.

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