Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

variadic template of a specific type

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) { ... }
};
like image 501
Zach Saw Avatar asked Nov 29 '12 22:11

Zach Saw


People also ask

What is a Variadic template C++?

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.

What is parameter pack in c++?

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.

What is Variadic function in C?

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.

How do you expand a parameter pack?

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 , .


3 Answers

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...
    }
};
like image 194
Lily Ballard Avatar answered Oct 05 '22 06:10

Lily Ballard


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).

like image 34
Kerrek SB Avatar answered Oct 05 '22 06:10

Kerrek SB


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};
like image 22
Olaf Dietsche Avatar answered Oct 05 '22 05:10

Olaf Dietsche