Reputation: 587
I am wondering if it's possible to initialize a std::array
of objects with an implicitly deleted default constructor, without knowing a priori the size of the array because it's a template argument and so having lost the possibility of using an initializer list. Code follows, it breaks with a "call to implicitly-deleted default constructor of std::array<A, 3UL>
"
struct A {
A (int b, int c) : mb(b), mc(c) { }
int mb;
int mc;
};
template <size_t NR_A>
struct B {
B (int b, int c) :
// <- how to initialize mAs here?
{ }
std::array<A, NR_A> mAs;
};
B<3> inst(1,1);
edit: I'd like to initialize all the A
's of mAs
to A{1,1}
Upvotes: 7
Views: 2593
Reputation: 11
here's a solution I came up with (requires c++17)
template<typename T, std::size_t N, std::size_t index_t = N, typename T2, typename... Ts>
constexpr auto make_array_same_ctor(T2 t, Ts... ts)
{
if constexpr (index_t <= 1) {
return std::array<T, N> {t, ts...};
} else {
return make_array_same_ctor<T, N, index_t - 1>(t, t, ts...);
}
}
This is a modification of one of the previous solution that had an array maker struct. This does the same just in a more concise form.
It takes in a single item and keeps doubling down on that item until it reaches a depth of 1,then returns an array from the unfolding. I didn't realize that you a fold expression can be used even though no arguments are passed through it. Although I know this implicitly from things like printf.
Upvotes: 1
Reputation: 24748
For both C++11 and C++14 (i.e.: pre-C++17) what you want can be achieved by means of template metaprogramming.
You could declare the following helper class template, array_maker<>
, which has a static
member function template, make_array
, that calls itself recursively:
template<typename T, std::size_t N, std::size_t Idx = N>
struct array_maker {
template<typename... Ts>
static std::array<T, N> make_array(const T& v, Ts...tail) {
return array_maker<T, N, Idx-1>::make_array(v, v, tail...);
}
};
Then, specialize this class template for the case Idx
equal to 1
, i.e.: the base case of the recursion:
template<typename T, std::size_t N>
struct array_maker<T, N, 1> {
template<typename... Ts>
static std::array<T, N> make_array(const T& v, Ts... tail) {
return std::array<T, N>{v, tail...};
}
};
Finally, it can be used in the constructor of your template this way:
template <size_t NR_A>
struct B {
B (int b, int c) : mAs{array_maker<A, NR_A>::make_array(A{b,c})}
{}
std::array<A, NR_A> mAs;
};
Upvotes: 4
Reputation: 21160
You may use delegating constructors and pack expansion
struct A {
A(int b, int c) : b(b), c(c) { }
A(const A&) = delete;
A(A&&) = delete;
int b;
int c;
};
template <size_t N>
struct B {
B (int b, int c) : B(b, c, std::make_index_sequence<N>{}) {}
template<size_t... Is>
B (int b, int c, std::index_sequence<Is...>) :
arr{(Is, A{b, c})...}
{}
std::array<A, N> arr;
};
Note if the move and copy constructors are deleted, this will only work after C++17.
Upvotes: 10