Mircea Ispas
Mircea Ispas

Reputation: 20790

How to build std::array like data structure from std::initializer_list

I want to implement a data structure with compile time constant size(like std::array). I want to be able to initialize this data structure like this:

MyStruct<3, int> example = {1, 2, 3};

This work fine using constructor like: MyStruct(std::initializer_list<T> elements), but the compiler doesn't enforce the same size for my internal structure and elements, even if they are both known at compile time.

I can't use static_assert because elements.size() is not compile time constant.

Is there any way to enforce at compile time same size for elements as in MyStruct?

Upvotes: 3

Views: 745

Answers (2)

Zulan
Zulan

Reputation: 22670

You could try a constructor using variadic templates:

template<std::size_t N, typename E>
struct MyStruct {
    int otherStuff;
    E values[N];

    template<typename ...TT>
    MyStruct(TT&&...t) : values{std::forward<TT>(t)...} {
        static_assert(N == sizeof...(t), "Size mismatch!");
        for (size_t i = 0; i < N; i++) std::cout << values[i] << ",";
        std::cout << std::endl;
    }
};

This does work as expected with:

MyStruct<3, int> example = {1,2,3};
MyStruct<3, int> exampleFail = {1,2}; //error: static assertion failed: Size mismatch!

Please note that there is still a difference between std:array and MyStruct when it comes to list-initialization:

MyStruct<3, int> exampleList{1,2,3}; // works
std::array<int, 3> arr = {1,2,3};    // works, but warning with clang++
std::array<int, 3> arrList{1,2,3};   // works with g++, does not compile with clang++

The reason is, that the single brace only works for std::array because of brace elision, which does not always apply as documented in an official defect report.

Upvotes: 2

sasha.sochka
sasha.sochka

Reputation: 14715

std::array does not have a constructor! And it doesn't use initializer_list, instead it uses uniform initialization. (similar to POD structures, actually std::array is POD structure).

So what std::array really does is very similar to the following:

template<int size, typename T>
struct array {
   T data[size];
   // and some member function here;
   // Warning! No constructors !
};

When, later, you write

std::array<3, int> arr = {1,2,3};

It is equivalent to

std::array<3, int> arr = {{1,2,3}};

And this is just POD initialization where {1,2,3} is assigned to data.


If you want to enforce having the same size checking in compile time it's possible using std::array itself instead of std::initializer_list.

If you change your initializations from {1,2,3} to std::array<3, int>{1, 2, 3} and constructor argument from std::initializer_list<T> to std::array<SIZE, T> user will be enforced to pass array with size SIZE.

Upvotes: 1

Related Questions