Search code examples
c++arraysconstructorstlinitializer-list

How can I initialize an std::array of a class without a default constructor?


Let's say I have a class without a default constructor called Foo.

If I were using an std::vector, I could do this:

std::vector<Foo> vec(100, Foo(5));

This would create a vector of 100 elements, each with value Foo(5).

How do I do the same with std::array<Foo, 100>?

I obviously do not want to list out Foo(5) explicitly 100 times in an initializer list. And yet I cannot wait till after the array is constructed to initialize it, since the lack of default constructor will produce a compiler error.

Bonus points for a solution that allows me to avoid the copy constructor as well, by supplying explicit constructor arguments similar to "placement new" or emplace functions.


Solution

  • With copy constructor, something along these lines:

    template <typename T, size_t... Is>
    std::array<T, sizeof...(Is)> MakeArrayHelper(
        const T& val, std::index_sequence<Is...>) {
      return {(static_cast<void>(Is), val) ...};
    }
    
    template <typename T, size_t N>
    std::array<T, N> MakeArray(const T& val) {
      return MakeArrayHelper<T>(val, std::make_index_sequence<N>{});
    }
    
    std::array<Foo, 100> arr = MakeArray<Foo, 100>(Foo(5));
    

    Actually, this can be done without copy constructor after all. This solution relies heavily on C++17's mandatory copy elision.

    template <typename T, size_t... Is, typename... Args>
    std::array<T, sizeof...(Is)> MakeArrayHelper(
        std::index_sequence<Is...>, Args&&... args) {
      return {(static_cast<void>(Is), T{std::forward<Args>(args)...}) ...};
    }
    
    template <typename T, size_t N, typename... Args>
    std::array<T, N> MakeArray(Args&&... args) {
      return MakeArrayHelper<T>(std::make_index_sequence<N>{},
                                std::forward<Args>(args)...);
    }
    

    Demo