Variadic templates одинаковое количество аргументов функции, как в классе

Как определить подпись метода, чтобы он принимал такое же количество аргументов, как определение класса вариационного шаблона? Например, как определить класс Array:

template<typename T, int... shape>
class Array
{
public:
    T& operator () (???);
};

Итак, вы сможете называть это следующим образом:

Array<int, 3, 4, 5> a;
a(1, 2, 3) = 2;

Ответ 1

template<class T, int...Shape>
class Array {
  template<int>using index_t=int; // can change this
public:
  T& operator()(index_t<Shape>... is);
};

или

template<class T, int...Shape>
class Array {
public:
  T& operator()(decltype(Shape)... is);
};

или

template<class T, int...Shape>
class Array {
public:
  T& operator()(decltype(Shape, int())... is);
};

если вы хотите изменить тип параметра, отличающийся от Shape.

Я считаю, что decltype сложнее понять прикосновение, чем using, особенно если вы хотите изменить тип параметра, отличный от int.

Другой подход:

template<class T, int...Shape>
class Array {
public:
  template<class...Args,class=typename std::enable_if<sizeof...(Args)==sizeof...(Shape)>::type>
  T& operator()(Args&&... is);
};

который использует SFINAE. Он не гарантирует, что Args являются целыми типами. Мы могли бы добавить другое предложение, если бы захотели (чтобы все Args были конвертированы в int, скажем).

Еще один подход заключается в том, чтобы ваш operator() принял пакет значений, например a std::array<sizeof...(Shape), int>. Вызывающие должны:

Array<double, 3,2,1> arr;
arr({0,0,0});

используйте набор {} s.

Окончательный подход:

template<class T, int...Shape>
class Array {
public:
  template<class...Args>
  auto operator()(Args&&... is) {
    static_assert( sizeof...(Args)==sizeof...(Shapes), "wrong number of array indexes" );
  }
};

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

Я бы порекомендовал отправку тегов, но я не вижу способа сделать его намного более чистым, чем решение SFINAE, с дополнительными decltype и всеми или более эффективными сообщениями об ошибках, чем версия static_assert, с другой стороны.

Ответ 2

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

template <int>
struct shape_helper { typedef int type; };

template <typename T, int... Shape>
class Array
{
public:
    T& operator()(typename shape_helper<Shape>::type...);
};