I have two classes A and B, where B contains an array of A.
I will get a compiler error of this constructor of B (complains that the default constructor of A cannot be referenced), unless the default constructor of A is not deleted. What I understand about this compiling issue is that, by writing this constructor of B, due to the lack of initialization list, compiler will assume to add a default constructor of A into it, which will cause an error since it’s deleted.
How can I get rid of this error?
constexpr int data[] = {1,2,3,4,5};
constexpr size_t N = sizeof(data)/sizeof(int);
struct A {
A() = delete; // error in B constructor if using this
// constexpr A() = default; // no error if using this instead
constexpr A(int a): a_(a) {}
private:
int a_ = 0;
};
struct B{
constexpr B() { // error: the default constructor of A cannot be referenced
for(size_t i = 0; i < N; i++) arr_[i] = A(data[i]);
}
private:
A arr_[N];
};
>Solution :
The direct solution when N is 5 is to just initialize arr_ in the member initializer list:
struct B {
constexpr B()
: arr_{A(data[0]), A(data[1]), A(data[2]), A(data[3]), A(data[4])} {}
private:
A arr_[N];
};
This becomes cumbersome if you need to change N or make it into a template parameter. You can generalize the initialization by using std::array and a lambda:
C++20 and later:
#include <array>
#include <utility>
struct B {
constexpr B()
: arr_{[]<std::size_t... I>(std::index_sequence<I...>) {
return std::array{A(data[I])...};
}(std::make_index_sequence<N>())} {}
private:
std::array<A, N> arr_;
};
In C++14 / C++17, you can’t use lambda templates so you can move that out to a helper function:
struct B {
private:
template<std::size_t... I>
static constexpr std::array<A, N> helper(std::index_sequence<I...>) {
return std::array<A, N>{A(data[I])...};
}
public:
constexpr B()
: arr_{helper(std::make_index_sequence<N>())} {}
private:
std::array<A, N> arr_;
};