I am exploring methods for storing arrays of literals (any type) without sidecar size information. I've sketched some approaches but for each have some lingering questions. This is the simplest method, a template over array size. Please note that this is a simple test case and that actual classes might have additional members, additional template parameters, and template parameters that must be provided.
template <size_t I>
struct A0 {
std::array<const char*, I> a;
};
The problem is that the size is detached from the string literals, making these sort of errors too easy:
// error, not caught
auto a0 = A0<3>{{"1","2"}};
While the following intialization is valid, because C 20 does not allow partial CTAD it cannot work if any template argument must be specified. Also it trades boilerplate for boilerplate: '<3>' in exchange for 'std::array'. Without specifying the type CTAD won't be able to deduce the template size parameter 'I'.
auto a0 = A0{std::array{"1","2"}};
Is there a deduction guide that allows:
auto a0 = A0{{"1","2"}};
CodePudding user response:
You could create a template constructor that that combined with a user defined deduction guidline either deduces the correct argument count or results in a compiler error, if the user specifies the incorrect number of parameters:
template <size_t I>
struct A0 {
template<class ...Args>
constexpr A0(Args...args)
: a{ args... }
{
static_assert(sizeof...(args) == I, "parameter count does not match array size");
}
std::array<const char*, I> a;
};
template<class ...Args>
A0(Args...) -> A0<sizeof...(Args)>;
constexpr A0 a{ "1", "2", "3" };
static_assert(std::is_same_v<std::remove_const_t<decltype(a)>, A0<3>>, "expectation mismatch");
constexpr A0<4> a1 { "1", "2", "3" }; // compiler error: parameter count does not match array size
CodePudding user response:
So, you are asking about auto a0 = A0{{"1","2"}};
, but first here is a deduction guide for auto a0 = A0{"1","2"};
:
template<typename... Ts>
A0(Ts...) -> A0<sizeof...(Ts)>;
I think it is clear what it does. It simply counts the number of arguments given.
For auto a0 = A0{{"1","2"}};
:
template<typename T, std::size_t N>
A0(const T(&)[N]) -> A0<N>;
This relies on the fact that a braced argument can be deduced to an array type by template argument deduction. The caveat here is that all the elements of the braced argument must have the same type.
If you want to use both of the deduction guides, you should constrain T
in the second one not to be const char
to make sure that the special case A0{"123"}
is not considered as array case. You might want to constrain the types anyway depending on what implicit conversions you want to allow in this form.
Both of these then, after the class template argument deduction, rely on aggregate initialization (the additional braces can be left out) to actually construct the class instance. But if you have constructors in the class instead, it will work the same.