如何通过子类型列表制作指针向量?

时间:2014-05-16 20:55:46

标签: c++ templates variadic-templates

我有一个基本类型B和它的子类型列表,如B1,B2,它们是从B派生的。我想开发一个函数来制作一个带有新B1,新B2和左右元素的指针vetor,像

template<class BaseType, class... SubTypes>
vector<unique_ptr<BaseType>> makePtrVector()
{
     vector<unique_ptr<BaseType>> v;
     v.emplace_back(new SubTypes)...; // error, SubTypes has to be expanded
     return v;
}

我尝试了上面的代码却失败了。我不清楚参数包的扩展。任何纠正它的方法或它只是一个编译器错误?

1 个答案:

答案 0 :(得分:2)

您只能在某些上下文中执行参数包扩展。完整的陈述级别不是其中之一。

std::vector::emplace_back返回void,因此我们可以执行此操作:

struct empty {};
template<class BaseType, class... SubTypes>
vector<unique_ptr<BaseType>> makePtrVector() {
  vector<unique_ptr<BaseType>> v;
  empty unused[]= {(v.emplace_back(new SubTypes), empty{})..., empty{}}; // error
  return v;
}

我们在其中创建empty类型的初始化列表,作为其构造的副作用,调用您喜欢的代码。 (int也很受欢迎)。

另一种解决方法是将其转移到辅助函数中。但是,这不适用于每个编译器:

template<typename...Fs>
void do_in_order( Fs&&... fs ) {
  empty unused[]= { (void( std::forward<Fs>(fs) ), empty{})..., empty{} };
}

然后在你的代码中:

template<class BaseType, class... SubTypes>
vector<unique_ptr<BaseType>> makePtrVector() {
  vector<unique_ptr<BaseType>> v;
  do_in_order( [&]{ v.emplace_back(new SubTypes); }... );
  return v;
}
看起来不那么钝的不是吗?缺点是一些主要的编译器不喜欢lambda中的不完整参数包,只有在lambda的紧密结束后才会扩展。