How to initialize std::array elegantly if T is not default constructible?

后端 未结 3 698
名媛妹妹
名媛妹妹 2020-11-27 05:59

How do I initialize std::array if T is not default constructible?

I know it\'s possible to initialize it like that:

相关标签:
3条回答
  • 2020-11-27 06:16

    Following will solve your issue:

    #if 1 // Not in C++11, but in C++1y (with a non linear better version)
    
    template <std::size_t ...> struct index_sequence {};
    
    template <std::size_t I, std::size_t ...Is>
    struct make_index_sequence : make_index_sequence<I - 1, I - 1, Is...> {};
    
    template <std::size_t ... Is>
    struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
    
    #endif
    
    namespace detail
    {
        template <typename T, std::size_t ... Is>
        constexpr std::array<T, sizeof...(Is)>
        create_array(T value, index_sequence<Is...>)
        {
            // cast Is to void to remove the warning: unused value
            return {{(static_cast<void>(Is), value)...}};
        }
    }
    
    template <std::size_t N, typename T>
    constexpr std::array<T, N> create_array(const T& value)
    {
        return detail::create_array(value, make_index_sequence<N>());
    }
    

    So test it:

    struct NoDefaultConstructible {
        constexpr NoDefaultConstructible(int i) : m_i(i) { }
        int m_i;
    };
    
    int main()
    {
        constexpr auto ar1 = create_array<10>(NoDefaultConstructible(42));
        constexpr std::array<NoDefaultConstructible, 10> ar2 = create_array<10>(NoDefaultConstructible(42));
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 06:20

    Sadly the existing answers here don't work for non-copyable types. So I took @Nawaz answer and modified it:

    #include <utility>
    #include <array>
    
    
    template<typename T, size_t...Ix, typename... Args>
    std::array<T, sizeof...(Ix)> repeat(std::index_sequence<Ix...>, Args &&... args) {
       return {{((void)Ix, T(args...))...}};
    }
    
    template<typename T, size_t N>
    class initialized_array: public std::array<T, N> {
    public:
        template<typename... Args>
        initialized_array(Args &&... args)
            : std::array<T, N>(repeat<T>(std::make_index_sequence<N>(), std::forward<Args>(args)...)) {}
    };
    

    Note that this is an std::array subclass so that one can easily write

    class A { 
        A(int, char) {}
    }
    
    ...
    
    class C {
        initialized_array<A, 5> data;
    
        ...
    
        C(): data(1, 'a') {}
    }
    

    Without repeating the type and size. Of course, this way can also be implemented as a function initialize_array.

    0 讨论(0)
  • 2020-11-27 06:37

    Given N, you could generate a sequence-type calledseq<0,1,2,3,...N-1> using a generator called genseq_t<>, then do this:

    template<typename T, int N>
    void f(T value)
    {
         //genseq_t<N> is seq<0,1,...N-1>
         std::array<T, N> items = repeat(value, genseq_t<N>{});
    }
    

    where repeat is defined as:

    template<typename T, int...N>
    auto repeat(T value, seq<N...>) -> std::array<T, sizeof...(N)> 
    {
       //unpack N, repeating `value` sizeof...(N) times
       //note that (X, value) evaluates to value
       return {(N, value)...}; 
    }
    

    And the rest is defined as:

    template<int ... N>
    struct seq
    {
       using type = seq<N...>;
    
       static const std::size_t size = sizeof ... (N);
    
       template<int I>
       struct push_back : seq<N..., I> {};
    };
    
    template<int N>
    struct genseq : genseq<N-1>::type::template push_back<N-1> {};
    
    template<>
    struct genseq<0> : seq<> {};
    
    template<int N>
    using genseq_t = typename genseq<N>::type;
    

    Online demo

    Hope that helps.

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