I want to accept up to a number of parameters (this number being defined in a template parameter) in a template class constructor. I can't use an initializer_list
, since I can't assert its size at compile time, as far as I know.
My first attempt was using a std::array as a parameter:
template<size_t s>
class foo {
int v[s];
public:
foo(std::array<int, s>) {/*...*/}
};
However, that forces me to initialize like this (even when the constructor is not explicit
) :
foo<4> a{{1,2,3,4}} // Two brackets.
I would think there may be some template magic (variadic templates?), but I can't even figure out the proper syntax to use in the constructor. I can't call a constructor recursively... can I?
I tried looking for a definition of the constructor for std::array
(since it doesn't allow for more parameters than the size of the array, just what I want), but all I could find is that it has implicit constructors. Is that the default constructors? If so, how does
std::array<int, 3> a = {1,2,3}
work?
Optional bonus: Why didn't the standard define a fixed size alternative to std::initializer_list
? Something like std::static_initializer_list<T, N>
. Are there any plans on supporting this kind of functionality in the future? Is it even needed?
You could create a variadic constructor and just assert that it was provided the right number of arguments:
template <size_t SZ>
struct Foo {
template <typename... Args>
Foo(Args... args) {
static_assert(sizeof...(Args) <= SZ, "Invalid number of arguments");
// ... stuff ...
}
};
So that:
Foo<3> f; // OK
Foo<3> f(1, 2, 3); // OK
Foo<3> f(1, 2, 3, 4, 5); // error
As an example to initialize an array, that could look like:
template <size_t SZ>
struct Foo {
template <typename... Args>
Foo(Args... args)
: v{{args...}}
{
static_assert(sizeof...(Args) <= SZ, "Invalid number of arguments");
}
std::array<int, SZ> v;
};
That constructs v
correctly as you'd expect, though if you try to pass more than SZ
args to Foo
's constructor, you'd see the error on initializing v
before the static_assert
.
For a clearer static_assert
error, you could delegate the top-level Foo
to private constructors that take an extra integral_constant
argument for whether or not they're valid constructors:
template <typename... Args>
Foo(Args... args)
: Foo(std::integral_constant<bool, sizeof...(Args) <= SZ>{},
args...)
{ }
private:
template <typename... Args>
Foo(std::true_type, Args... args)
: v{{args...}}
{ }
template <typename False, typename... Args>
Foo(False, Args... )
{
// False is only ever std::false_type
static_assert(False::value, "Invalid number of arguments!");
}
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