променливи шаблони същия брой аргументи на функцията като в класа

Как да дефинирам сигнатура на метод, така че да приема същия брой аргументи като дефиницията на променлив шаблонен клас? Например как да дефинираме клас Array:

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

Така че ще можете да го наречете така:

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

person DikobrAz    schedule 05.11.2014    source източник
comment
Мисля, че това трябва да работи: T& operator()(decltype(shape)... args);   -  person 0x499602D2    schedule 05.11.2014
comment
@0x499602D2 Което се обобщава до: T& operator()(decltype(shape, X())... args); за промяна на типа.   -  person Deduplicator    schedule 05.11.2014
comment
@Deduplicator Наистина. Но предположих, че иска int.   -  person 0x499602D2    schedule 05.11.2014
comment
Страхотно, всички подходи работят, благодаря на всички!   -  person DikobrAz    schedule 05.11.2014
comment
Ако просто търсите многоизмерна версия на std::array, може да успеете да си спестите малко работа с трик с псевдоним на променлив масив.   -  person Casey    schedule 06.11.2014
comment
@Casey чист трик, но контекстът, в който работя, е малко по-различен. Мислех, че масивът е просто чист пример.   -  person DikobrAz    schedule 06.11.2014


Отговори (2)


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

or:

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

or:

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() да вземе пакет от стойности, като 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 от друга страна.

person Yakk - Adam Nevraumont    schedule 05.11.2014
comment
Изглежда, че проверката за преобразуване на int е излишна, Array‹int, 1, 2, 3› a; a((char)1, (short)2, 2.0f); работи така или иначе. - person DikobrAz; 05.11.2014
comment
@DikobrAz страхът би бил Array<int, 1,2,3> a; a( "hello", "world", 3 ); също ще проработи и след това ще се счупи в тялото на operator(), което не е идеално. Ако имате друг код за метапрограмиране, който пита можете ли да бъдете извикан с тези аргументи, предпочитате да не лъжете за това. - person Yakk - Adam Nevraumont; 05.11.2014
comment
Човек не трябва да пренебрегва факта, че всеки къс, който не трябва да предполага, търси или все още помни, означава, че много повече мозъчна сила е налична за други неща. Което ме кара да предпочитам decltype. (Въпреки че не е толкова лошо, когато дефиницията на допълнителния тип е точно над неговия сайт за използване.) - person Deduplicator; 05.11.2014
comment
@Yakk Виждам смисъла, погрешно го сравних с първоначалния подход с decltype. - person DikobrAz; 05.11.2014
comment
Бих казал, че типовете на индексите трябва да бъдат същите като типовете на екстентите, което прави втората опция тук правилна. - person Casey; 06.11.2014
comment
Между другото, изглежда, че в gcc 4.6 работи само първият подход, goo.gl/e7CNBC, goo.gl/JHZO0n - ако не използвате шаблонен псевдоним, а typedef като в stackoverflow.com/a/26766667/1008902 - person DikobrAz; 06.11.2014
comment
Има ли възможност да компилираме това с помощта на визуално студио? Само подходът SFINAE изглежда работи. - person DikobrAz; 29.12.2014
comment
@diko тествахте всичките 7 споменати подхода и само един работи във VS2015? Е, това изглежда правдоподобно. - person Yakk - Adam Nevraumont; 29.12.2014

Предполагам, че искате всичките ви аргументи да са от един и същи тип, вероятно с целочислен тип (просто ще използвам int). Лесен подход е да използвате пакета с параметри, който вече имате:

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

template <typename T, int... Shape>
class Array
{
public:
    T& operator()(typename shape_helper<Shape>::type...);
}; 
person Dietmar Kühl    schedule 05.11.2014
comment
По някакъв начин предпочитам да не въвеждам допълнителни типове. Харесва се коментирано тук: stackoverflow.com/questions/26766617/ - person Deduplicator; 05.11.2014
comment
@0x499602D2: да, типът беше умишлен. - person Dietmar Kühl; 05.11.2014
comment
@Deduplicator: да, използването на decltype(shape, X())... е добър трик. - person Dietmar Kühl; 05.11.2014