可变模板和new

Variadic templates and new

本文关键字:new      更新时间:2023-10-16

我有这样一个类模板:

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 .

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;
}

可以完成此操作,但由于您直接编写表达式,因此有点迂回。您需要为可变模板参数列表中的每个参数调用一次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);

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

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

与此相关的是,在这种特殊情况下,您可以通过如下方式编写构造函数来使用vector的initializer_list支持

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

或者由于某种原因不能使用构造函数初始化式

时使用赋值
Test() {
  test_cases = { new T ... };
}

也许你想要一个元组在你的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;
}

我突然想到你想要一个任何类型的动态向量(虽然我自己没有亲自去看,但我的一个朋友告诉我在boost库中显然有类似的东西),而不是模板向量。

模板vector基本上是可以采用一种定义类型中的任何一种的vector(要么全是整型,要么全是双精度,要么全是浮点数,但不是整型、双精度和浮点数)。

通常没有这样的类的原因是每个项在内存中占用不同的块大小(char是一个字节,int可能是4个字节等),并且在查找时需要额外的资源才能知道会发生什么(正常的存储是连续的……如果vector '基本上'是一个数组,那么它是什么呢?

如果你想构建自己的(我尝试过),你会看到void *指针,动态内存分配和一大堆涉及类型转换的头痛问题(我不知道有任何自动方法可以在幕后正确地对项目进行类型转换,但其他人可能能够参与)。

相关文章:
  • 没有找到相关文章