I want a variadic template that simply accepts unsigned integers. However, I couldn't get the following to work.
struct Array
{
template <typename... Sizes> // this works
// template <unsigned... Sizes> -- this does not work (GCC 4.7.2)
Array(Sizes... sizes)
{
// This causes narrowing conversion warning if signed int is supplied.
unsigned args[] = { sizes... };
// ...snipped...
}
};
int main()
{
Array arr(1, 1);
}
Any help appreciated.
EDIT: In case you're wondering, I'm trying to use variadic template to replicate the following.
struct Array
{
Array(unsigned size1) { ... }
Array(unsigned size1, unsigned size2) { ... }
Array(unsigned size1, unsigned size2, unsigned size3) { ... }
// ...
Array(unsigned size1, unsigned size2, ..., unsigned sizeN) { ... }
};
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
A function parameter pack is a function parameter that accepts zero or more function arguments. A template with at least one parameter pack is called a variadic template.
Variadic functions are functions that can take a variable number of arguments. In C programming, a variadic function adds flexibility to the program. It takes one fixed argument and then any number of arguments can be passed.
Parameter packs can only be expanded in a strictly-defined list of contexts, and operator , is not one of them. In other words, it's not possible to use pack expansion to generate an expression consisting of a series of subexpressions delimited by operator , .
I'm not sure why you expected that to work. Clang tells me the error is unknown type name 'Sizes'
in the declaration of the constructor. Which is to be expected, since Sizes
isn't a type (or rather, a template pack of types), it's a template pack of values.
It's unclear what exactly you're trying to do here. If you pass integral values in as template parameters, what are the constructor parameters supposed to be?
Update: With your new code all you need is a static_cast<unsigned>()
.
struct Array
{
template <typename... Sizes> // this works
Array(Sizes... sizes)
{
unsigned args[] = { static_cast<unsigned>(sizes)... };
// ...snipped...
}
};
If you want to accept dynamic arguments that must all be integers, you want an ordinary typename template, but check that all the types are (convertible to) unsigned integers:
#include <type_traits>
struct Array
{
template <typename ...Args>
explicit Array(Args ...args,
typename std::enable_if<all_int<Args...>::value>::type * = nullptr);
// ...
};
Now you just need the trait:
template <typename...> struct all_int;
template <> struct all_int<> : std::true_type { };
template <typename T, typename ...Rest> struct all_int<T, Rest...>
: std::integral_constant<bool,
std::is_convertible<T, unsigned int>::value && all_int<Rest>::value>
{ }
If you prefer to make the types strict, you can also use is_same
instead of is_convertible
.
Another option is to forgo variadic templates entirely and make your class list-initializable by accepting a single std::initializer_list<unsigned int>
, which provides considerably better numeric safety (for instance, narrowing conversions are forbidden).
Look into initializer list
You could specify it like
struct Array
{
Array(std::initializer_list<unsigned> sizes)
{
for (auto i = sizes.begin(); i != sizes.end(); ++i)
...
}
}
Although, usage would change to
Array arr = {1, 1};
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