Функция с фиксированным количеством параметров, определяемая целым числом
У меня есть класс с шаблоном, который принимает целое число:
template <unsigned int N>
class Example {};
Я ищу способ определения функции (члена), которая принимает в качестве аргументов некоторое количество объектов Example
. Сумма должна быть определена N
, поэтому функция будет использоваться следующим образом:
Function(Example<2>(), Example<2>());
Function(Example<3>(), Example<3>(), Example<3>());
То, что я пробовал до сих пор:
Используя список инициализаторов, один может передать набор объектов функции:
template <unsigned int N>
void Function(std::initializer_list<Example<N>> list);
//...
Function({Example<2>(), Example<2>()});
Однако проблема, кроме того, что передается только один аргумент (список), заключается в том, что с помощью этого метода можно использовать любое количество аргументов:
Function({Example<2>()});
Я также попытался использовать вариационную функцию:
template <unsigned int N>
void Function(Example<N> e...)
{
va_list args;
va_start(args, e);
//...
}
Function(Example<2>(), Example<2>());
Это позволяет использовать реальные параметры, но проблема использования любого количества аргументов остается, и невозможно узнать, сколько аргументов было фактически передано.
Ответы
Ответ 1
Предполагая, что вы хотите, чтобы количество аргументов выводилось из типа Example<N>
и что все Example<I>
должны делиться одним и тем же таким N
, решение С++ 17 может быть
template <unsigned int... I>
auto Function( Example<I>... ) ->
std::enable_if_t<( ( I == sizeof...(I) ) && ... )>
{
// or static_assert() if you always want an error
}
Ответ 2
Сделайте Function
вариационный шаблон и используйте std::enable_if_t
, чтобы ограничить его:
-
Некоторая черта IsExample
может использоваться, чтобы убедиться, что все аргументы являются экземплярами Example
-
sizeof...(pack)
можно использовать для получения размера пакета параметров
template <unsigned int N, typename... Ts>
auto Function(Ts... xs)
-> std::enable_if_t<(IsExample<Ts>::value && ...)
&& (sizeof...(Ts) == N)>
{
}
живой пример в wandbox
Ответ 3
Вы должны использовать шаблон вариационной функции с static_assert. В отличие от подходов, связанных с enable_if
, это приведет к сообщению об ошибке readable, если переданы неверные аргументы.
template<unsigned int ... I>
void Function(Example<I>... items)
{
static_assert
(
true && (... && (static_cast<unsigned int>(sizeof...(I)) == I))
, "This function accepts N arguments of type Example<N>"
);
}
Онлайн-компилятор
Ответ 4
Есть много ответов, которые затрагивают дружественные ограничения SFINAE, но мне не нравится, когда мой SFINAE в возвращаемом значении:
template <unsigned int... Is,
std::enable_if_t<( ( Is == sizeof...(Is) ) && ... ), bool> = true
>
void Function( Example<Is>... examples )
{
// code
}
или
template<bool b>
using test_requirement = std::enable_if_t<b, bool>;
template <unsigned int... Is,
test_requirement<( ( Is == sizeof...(Is) ) && ... )> = true
>
void Function( Example<Is>... examples )
{
// code
}
Ответ 5
+1 для элегантного решения Massimiliano Janes.
К сожалению, использование folding работает только для С++ 17.
Чтобы проверить, с С++ 11/С++ 14, что все I
равны sizeof...(I)
(и, возможно, что sizeof...(I)
равно N
, где N
- шаблон шаблона аргумент), достаточно проверить, что переменный тип, который получает неподписанные значения, является одним и тем же типом с другим порядком значений.
Я имею в виду: объявление тривиальной структуры как
template <std::size_t ... Is>
struct IList;
тест может быть
std::is_same<IList<N, sizeof...(Is), Is...>,
IList<sizeof...(Is), Is..., N>>::value
Начиная с С++ 14 можно использовать std::index_sequence
вместо IList
.
Итак, Example
может быть записано как
template <unsigned int N>
struct Example
{
template <unsigned int ... Is>
auto Function (Example<Is> ...)
-> typename std::enable_if<
std::is_same<IList<N, sizeof...(Is), Is...>,
IList<sizeof...(Is), Is..., N>>::value>::type
{ /* do something */ }
};
Ниже приведен пример использования (но не забудьте включить <type_traits>
)
int main()
{
Example<1U> e1;
Example<2U> e2;
// e1.Function(); // error
e1.Function(Example<1>{}); // compile
//e1.Function(Example<1>{}, Example<1>{}); // error
// e2.Function(); // error
//e2.Function(Example<2>{}); // error
e2.Function(Example<2>{}, Example<2>{}); // compile
//e2.Function(Example<2>{}, Example<2>{}, Example<2>{}); // error
}