5

I need to initialize a compile-time sized class array with an initializer_list. I'm already aware that I could use a parameter pack constructor and initialize it on the spot, but I need to use an initializer_list in this case. I'd also like to avoid dynamically initializing the array if possible. Here's the pseudo-code:

template<typename Type, std::size_t Length>
class Test
{
    public:
        Test(const std::initializer_list<Type> args)
        :   m_tData(args) //<-- Doesn't work of course
        {}

    private:
        Type m_tData[Length];
};

Of course, for non-const types, I could do

Test(const std::initializer_list<Type> args)
{
    std::copy(args.start(), args.end(), m_tData);
}

but this doesn't work if I try to use a const class like Test<const int, 3>({1, 2, 3}). Am I forced to use template specialization to have the internal array not actually be const so I can initialize it from the constructor body?

I'm using C++20. I've seen How do I initialize a member array with an initializer_list? but was wondering if anything has changed since then.

code-gs
  • 856
  • 6
  • 9
  • I don't think there is any way to initialize an array with an `std::initializer_list`. – Nikos C. Jun 04 '19 at 15:55
  • @NikosC., you can use expansion of a pack `` : `m_tData{(*(list.begin() + ii)), ...}`. – Evg Jun 04 '19 at 15:59
  • @NathanOliver This is not a dupe. The OP explicitly states that: "but I need to use an initializer_list in this case" – Nikos C. Jun 04 '19 at 16:05
  • 1
    @NikosC., I don't understand why it doesn't work. Take a look at my answer. The only thing you can't do is to check the size of the `initializer_list` at compile-time. – Evg Jun 04 '19 at 16:15
  • @Evg It does work indeed! That's quite neat. – Nikos C. Jun 04 '19 at 16:21

1 Answers1

5

Possible solution:

template<typename Type, std::size_t Length>
class Test
{
public:
    Test(std::initializer_list<Type> args) :
        Test(args, std::make_index_sequence<Length>{})      
    {
        assert(args.size() == Length);
    }

private:
    template<std::size_t... ii>
    Test(std::initializer_list<Type> args, std::index_sequence<ii...>) :
        m_tData{(*(args.begin() + ii))...}
    {}

    Type m_tData[Length];
};

https://godbolt.org/z/d8Zg3q

Is there a way I could still support initializer_lists of lesser lengths than the templated length? Say something like Test<const int, 5>({1, 2, 3})?

If the remaining elements are supposed to be initialized with Type{}, just a slight modification is needed:

Test(std::initializer_list<Type> args) :
    Test(args, std::make_index_sequence<Length>{})      
{
    assert(args.size() <= Length);
}

template<std::size_t... ii>
Test(std::initializer_list<Type> args, std::index_sequence<ii...>) :
    m_tData{(ii < args.size() ? *(args.begin() + ii) : Type{})...}
{}
Evg
  • 25,259
  • 5
  • 41
  • 83
  • Nice solution. Is there a way I could still support initializer_lists of lesser lengths than the templated length? Say something like ``Test({1, 2, 3})``? – code-gs Jun 04 '19 at 22:13