Вариационный шаблон определенного типа
Мне нужен вариационный шаблон, который просто принимает целые числа без знака.
Однако я не мог заставить следующее работать.
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) { ... }
};
Ответы
Ответ 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};