Variadic шаблоны и новые - PullRequest
       2

Variadic шаблоны и новые

7 голосов
/ 18 сентября 2011

У меня есть этот шаблон класса:

template<class... T>
class Test {
  std::vector<TestCase*> test_cases;
public:
  Test() {
    // Here, for each T an instance should be added to test_cases.
    test_cases.push_back((new T)...);
  }
};

Это прекрасно работает для одного аргумента шаблона, но для нескольких аргументов я получаю эту ошибку:

error: too many arguments to function call, expected 1, have 2

Как я могу использовать шаблоны с переменным числом аргументовс new таким образом?Какой правильный синтаксис?


РЕДАКТИРОВАТЬ: Я думаю, что мой вопрос не был достаточно ясным.Я хочу вот что:

Test<TestCase1, TestCase2, TestCase3>;
// The constructor will then be:
test_cases.push_back(new TestCase1);
test_cases.push_back(new TestCase2);
test_cases.push_back(new TestCase3);

Мой компилятор clang 163.7.1 с этим флагом: -std=c++0x.

Ответы [ 6 ]

3 голосов
/ 19 сентября 2011

vector::push_back ожидает один параметр, поэтому вы не можете развернуть шаблон переменной в вызове функции. Также я добавил параметр шаблона для базового класса (из которого происходят все остальные классы).

Вот что-то, что компилирует .

struct base{};
struct d0 : base{};
struct d1 : base{};
struct d2 : base{};

#include <vector>

// termination condition for helper function
template <class T>
void add(std::vector<T*>&) { 
}

// helper function
template <class T, class Head, class... Tail>
void add(std::vector<T*>& v) { 
       v.push_back(new Head()); 
       add<T, Tail...>(v);
}

template <class T, class ... U>
class test
{
    std::vector<T*> vec;
public:
    test() {
       add<T, U...>(vec);      
    }
};

int main() 
{
    test<base, d0,d1,d2> t;
}
2 голосов
/ 19 сентября 2011

Вы можете выполнить это, но это немного обходно, так как вы пишете выражение напрямую.Вам нужно вызвать push_back один раз для каждого аргумента в списке аргументов шаблона переменной.

Как этого добиться?Итак, вызывая рекурсивную функцию один раз для каждого аргумента шаблона:

template <typename Base, typename T1, typename T2, typename... T>
void fill(std::vector<Base*>& vec) {
    vec.push_back(new T1);
    fill<Base, T2, T...>(vec);
}

template <typename Base, typename T1>
void fill(std::vector<Base*>& vec) {
    vec.push_back(new T1);
}

Здесь у нас есть две перегрузки функции fill, одна со списком аргументов шаблона с переменным числом аргументов, а другая без - это база рекурсиидело.Пока есть еще как минимум два аргумента шаблона, вызывается первая версия.Если остался только один аргумент, вместо него вызывается второй аргумент.

Вызовите его так в конструкторе:

fill<TestCase, T...>(test_cases);
2 голосов
/ 19 сентября 2011

Расширение пакета может происходить только в выбранном количестве ситуаций и не работает для произвольных выражений или операторов.Тем не менее, поскольку одна из таких ситуаций - инициализация списка, и так как порядок операций определен для фигурных инициализаторов синтаксиса инициализации списка, всегда можно расширить произвольный оператор.То есть:

typedef std::initializer_list<int> expand;
expand { ( test_cases.push_back(new T), void(), 0 )... };

Хитрость void() заключается в подавлении любого вызова перегруженного operator,.Здесь совершенно не имеет значения, но я включил его, поскольку он может быть полезен при рефакторинге функциональности в макросе:

#define EXPAND( exp ) \
    std::initializer_list<int> { ( (exp), void(), 0 )... }

// No use of '...', it's in the macro body
EXPAND(( test_cases.push_back(new T) ));
1 голос
/ 19 сентября 2011

В связанной заметке, в данном конкретном случае вы можете использовать векторную поддержку initializer_list, написав конструктор следующим образом

Test()
:test_cases{ new T ... }
{ }

Или с помощью присваивания, если по какой-либо причине вы не можете использовать инициализаторы конструктора

Test() {
  test_cases = { new T ... };
}
0 голосов
/ 18 сентября 2011

Мне кажется, что вам нужен динамический вектор типа любого (хотя я лично не смотрю на себя, мой друг сказал мне, что в библиотеке буста, по-видимому, что-то вроде этого), в отличие от шаблонаvector.

Шаблонный вектор - это, по сути, вектор, который может принимать любой из one определенного типа (все целые или двойные числа, или все числа с плавающей запятой, но не целые числа, числа с двойными числами и числа с плавающей запятой).

Причина, по которой обычно нет такого класса, заключается в том, что каждый элемент занимает в памяти различный размер блока (char - это байт, int может быть 4 байта и т. Д.), И для этого потребуетсядополнительные ресурсы при поиске, чтобы узнать, чего ожидать (нормальное хранилище является смежным ... который представляет собой вектор, учитывая, что это "в основном" массив).

Если вы хотите создать свой собственный (Я пытался), вы смотрите на указатели void *, динамическое выделение памяти и целый ряд головных болей, связанных с приведением типов (я не знаю ни одного автоматизированного метода для правильной типизацииза кулисами, но другие могут скинуться).

0 голосов
/ 18 сентября 2011

Может быть, вы хотите кортеж внутри своего std :: vector?Не уверен, что это именно то, что вы хотели, но это компилируется по крайней мере на моем G ++ 4.6.1: D

#include <vector>
#include <utility>
#include <functional>
#include <string>

template<class... T>
class Test {
  std::vector<std::tuple<T*...>> test_cases;
public:
  Test() {
    // Here, for each T an instance should be added to test_cases.
    test_cases.push_back(std::tuple<T*...>((new T)...));
  }
};

int main()
{
   Test<int, float> foo;
   Test<std::string, double> bar;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...