This is an interesting problem I'm thinking about a time ago.
Given a struct
with an underlying aggregate:
#include <array>
template <typename T, size_t N>
struct A
{
constexpr A() = default;
template <typename ... Ts>
constexpr A(const T& value, const Ts& ... values); // magic
std::array<T, N> arr; // aggregate
};
How would you implement variadic template constructor A(const T& value, const Ts& ... values)
to
T
and another A<T, N>
Satisfying the above requirements, it is possible to do the following:
int main()
{
A<int, 3> x(1, 2, 3);
A<int, 2> y(1, 2);
A<int, 6> a(x, 1, 2, 3);
A<int, 6> b(1, x, 2, 3);
A<int, 6> c(1, 2, x, 3);
A<int, 6> d(1, 2, 3, x);
A<int, 6> e(x, x);
A<int, 6> f(y, y, y);
return 0;
}
Here's one approach that works, but could almost certainly be improved on.
We have a constructor for A
that takes a parameter pack, converts each element into a tuple, concatenates the tuples together to make for one large tuple, and then simply uses aggregate initialization from that big tuple. All of the following can be constexpr
, I just omitted it for brevity.
First we do the conversion:
template <class... Us>
A(Us const&... us)
: A(std::tuple_cat(as_tuple(us)...))
{ }
With:
// single argument
template <class U>
auto as_tuple(U const& u) {
return std::forward_as_tuple(u);
}
// aggregate argument
template <size_t M>
auto as_tuple(A<T, M> const& a) {
return as_tuple(a, std::make_index_sequence<M>{});
}
template <size_t M, size_t... Is>
auto as_tuple(A<T, M> const& a, std::index_sequence<Is...> ) {
return std::forward_as_tuple(std::get<Is>(a.arr)...);
}
And then we just initialize from there:
template <class... Us, class = std::enable_if_t<(sizeof...(Us) <= N)>>
A(std::tuple<Us...> const& t)
: A(t, std::index_sequence_for<Us...>{})
{ }
template <class... Us, size_t... Is>
A(std::tuple<Us...> const& t, std::index_sequence<Is...> )
: arr{{std::get<Is>(t)...}}
{ }
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With