darune
darune

Reputation: 11000

How to avoid retyping type information for aggregate initialization?

I have the following example:

#include <array>

struct A {
    const char* str;
    const char* str2;
};

template<size_t N>
struct As {
    std::array<A,N> elems_;
};


template<class... Args>
As(Args...)->As<sizeof...(Args)>; //<-- NOTE: deduction guide !


constexpr static As as{A{"a","b"}, A{"1","2"}};//<-- 'retyping' A here


int main() {
  return as.elems_.size(); 
}

Link to non-working example

While this codes works, I would like to avoid the 'retyping' of A's inside the aggregate list, but if I leave it out the deduction guide fails with: "cannot deduce template arguments for 'As'" (which, I guess makes sense). Maybe one way to fix this would be by hand-writing whatever number of deduction guides i need since then I could write the A type in each deduction guide (that is: one deduction for each size I need of the container).

Upvotes: 4

Views: 188

Answers (1)

Max Langhof
Max Langhof

Reputation: 23701

Nested aggregate initialization has the more or less surprising behavior that you don't add nested {/} to match.

For example, this is how you initialize std::array<std::array<int, 2>, 2>:

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

Doing { {1, 1}, {2, 2} } does not work!

Similarly, aggregate initialization in your case would require no nested {/} with CTAD already done (well, the template parameter given):

constexpr static As<2> as{"a","b", "1","2"}; // Ok!

Knowing that, we can just add the following deduction guide:

template<class ... Ts>
As(Ts...) -> As<(sizeof...(Ts) + 1)/2>;

This chooses N as "half the number of arguments". And indeed, now you can initialize like this:

constexpr static As as{"a","b", "1","2"}; // Deduces N = 2.

Demo: https://godbolt.org/z/oznEwl

Yes, this loses the ability to structure the input with {/} for better readability, but I blame aggregate initialization here (see nested array example above).

Upvotes: 5

Related Questions