7

我有这个类模板:

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.

4

6 回答 6

3

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;
}
于 2011-09-18T20:13:15.337 回答
2

包扩展只能在选定数量的情况下发生,并且不适用于任意表达式或语句。但是,由于其中一种情况是列表初始化,并且由于为列表初始化语法的大括号初始化程序定义了操作顺序,因此始终可以扩展任意语句。以机智:

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) ));
于 2011-09-18T20:00:47.537 回答
2

可以做到这一点,但它有点迂回,因为你直接写了表达式。您需要为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);
于 2011-09-18T20:03:34.760 回答
1

在相关说明中,在这种特殊情况下,您可以initializer_list通过编写构造函数来使用向量的支持,如下所示

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

或者如果由于任何原因您不能使用构造函数初始化程序,则使用赋值

Test() {
  test_cases = { new T ... };
}
于 2011-09-18T20:32:47.100 回答
0

也许你想在你的 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;
}
于 2011-09-18T19:24:10.407 回答
0

令我震惊的是,您想要一个任何类型的动态向量(尽管我不是亲自看我,但朋友告诉我在 boost 库中显然有类似的东西),而不是模板向量。

模板向量基本上是一个向量,可以采用任何一种定义的类型(所有整数,或所有双精度数,或所有浮点数,但不是整数、双精度数和浮点数)。

通常没有这样的类的原因是因为每个项目在内存中占用不同的块大小(一个 char 是一个字节,一个 int 可能是 4 个字节等),并且在查找时会占用额外的资源知道会发生什么(正常存储是连续的......这是一个向量,因为它“基本上”是一个数组)。

如果您正在寻找构建自己的(我尝试过),那么您正在查看 void * 指针、动态内存分配以及涉及类型转换的一系列令人头疼的问题(我不知道有任何自动化方法可以在幕后正确地对项目进行类型转换,但其他人可能能够参与)。

于 2011-09-18T19:44:32.413 回答