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

Функция с фиксированным количеством параметров, определяемая целым числом

У меня есть класс с шаблоном, который принимает целое число:

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

Это позволяет использовать реальные параметры, но проблема использования любого количества аргументов остается, и невозможно узнать, сколько аргументов было фактически передано.

4b9b3361

Ответ 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
 }