Universal aggregate initialization by variation patterns

This is an interesting problem that I am thinking about right now. Given a struct with a base 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 to implement the Variadic template A(const T& value, const Ts& ... values) constructor A(const T& value, const Ts& ... values) before

  • take both values ​​of type T and the other A<T, N>
  • correctly initialize the base aggregate based on the values ​​represented by the passed arguments
  • respect unit capacity
  • support C ++ 14 constexpr rules and not enter service data at runtime

Satisfying the above requirements, you can 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; } 
+8
c ++ c ++ 11 templates c ++ 14 variadic-templates
source share
2 answers

Here's one approach that works, but can almost certainly be improved.

We have a constructor for A that takes a package of parameters, converts each element into a tuple, concatenates the tuples together to make one big tuple, and then just uses aggregate initialization from that big tuple. All of the following could 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)...)) { } 

FROM

 // 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

+9
source share

@Barry's answer is definitely correct and acceptable. But this requires some additions to the C ++ 14 library (which you might also be able to write yourself in C ++ 11), and generally require some good tuple - and metaprogramming fu.

Let's look at a few arguments as a "range of ranges", where the range is just a pointer and size. Scalar arguments are only size-1 range, and A<T, N> arguments are size-N ranges.

 template<class T> struct Range { T const* data_; std::size_t size_; constexpr T const* begin() const noexcept { return data_; } constexpr T const* end() const noexcept { return data_ + size_; } constexpr std::size_t size() const noexcept { return size_; } }; template<class T> constexpr Range<T> as_range(T const& t) { return { &t, 1 }; } template<class T, std::size_t N> struct A; template<class T, std::size_t N> constexpr Range<T> as_range(A<T, N> const& a) { return { a.arr, N }; } 

Then you can simply do a double loop on all elements of all ranges

 template <typename T, size_t N> struct A { T arr[N]; // aggregate constexpr A() = default; template <typename U, typename... Us> constexpr A(U const u, Us const&... us) : arr{} { Range<T> rngs[1 + sizeof...(Us)] { as_range(u), as_range(us)... }; auto i = 0; for (auto const& r : rngs) for (auto const& elem : r) arr[i++] = elem; assert(i == N); } }; 

Live Example running at compile time (requires GCC> = 6.0 or Clang> = 3.4)

 template <class T, size_t N> void print(A<T, N> const& a) { for (T const& t : a.arr) { std::cout << t << ' '; } std::cout << '\n'; } int main() { constexpr A<int, 3> x(1, 2, 3); constexpr A<int, 2> y(1, 2); constexpr A<int, 6> a(x, 1, 2, 3); constexpr A<int, 6> b(1, x, 2, 3); constexpr A<int, 6> c(1, 2, x, 3); constexpr A<int, 6> d(1, 2, 3, x); constexpr A<int, 6> e(x, x); constexpr A<int, 6> f(y, y, y); print(a); // 1 2 3 1 2 3 print(b); // 1 1 2 3 2 3 print(c); // 1 2 1 2 3 3 print(d); // 1 2 3 1 2 3 print(e); // 1 2 3 1 2 3 print(f); // 1 2 1 2 1 2 } 
+5
source share

All Articles