Подтвердить что ты не робот

Вариационный шаблон определенного типа

Мне нужен вариационный шаблон, который просто принимает целые числа без знака. Однако я не мог заставить следующее работать.

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);
}

Любая помощь была оценена.

EDIT: Если вам интересно, я пытаюсь использовать вариационный шаблон для репликации следующих.

struct Array
{
    Array(unsigned size1) { ... }
    Array(unsigned size1, unsigned size2) { ... }
    Array(unsigned size1, unsigned size2, unsigned size3) { ... }
    // ...
    Array(unsigned size1, unsigned size2, ..., unsigned sizeN) { ... }
};
4b9b3361

Ответ 1

Я не уверен, почему вы ожидали, что это сработает. Clang говорит мне, что ошибка unknown type name 'Sizes' в объявлении конструктора. Этого следует ожидать, поскольку Sizes не является типом (точнее, пакетом шаблонов типов), это набор шаблонов значений.

Непонятно, что именно вы пытаетесь сделать здесь. Если вы передаете интегральные значения в качестве параметров шаблона, каковы должны быть параметры конструктора?


Обновить. С вашим новым кодом все, что вам нужно, это static_cast<unsigned>().

struct Array
{
    template <typename... Sizes> // this works
    Array(Sizes... sizes)
    {
        unsigned args[] = { static_cast<unsigned>(sizes)... };
        // ...snipped...
    }
};

Ответ 2

Если вы хотите принять динамические аргументы, которые должны быть целыми, вам нужен обычный шаблон имен, но убедитесь, что все типы являются (конвертируемыми) целыми числами без знака:

#include <type_traits>

struct Array
{
    template <typename ...Args>
    explicit Array(Args ...args,
        typename std::enable_if<all_int<Args...>::value>::type * = nullptr);

    // ...
};

Теперь вам просто нужна черта:

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>
{ }

Если вы предпочитаете делать типы строгими, вы можете также использовать is_same вместо is_convertible.

Другой вариант заключается в том, чтобы полностью отказаться от вариативных шаблонов и сделать ваш список классов инициализированным, приняв один std::initializer_list<unsigned int>, который обеспечивает значительно лучшую числовую безопасность (например, сужение конверсий запрещено).

Ответ 3

Посмотрите список инициализаторов

Вы можете указать его как

struct Array
{
    Array(std::initializer_list<unsigned> sizes)
    {
        for (auto i = sizes.begin(); i != sizes.end(); ++i)
            ...
    }
}

Хотя, использование изменилось бы на

Array arr = {1, 1};