如何扩展对可变参数模板基类的调用?

时间:2015-05-31 22:10:30

标签: c++ templates c++11 metaprogramming variadic-templates

我有一组非正交的策略,它们都实现了一个通用的命名方法,策略添加了安全检查。 我希望用户能够组合策略以允许更复杂的验证,而无需手动为每个组合案例创建策略。 我的方法是创建一个新的策略类来组合其他人。

下面的简化示例将C显示为组合类,此处将方法ID组合在一起。当在C上调用id时,预期的结果是顺序调用每个基类的id。

#include <iostream>
using namespace std;

struct A 
{
    void id() { cout << "A ";}
};

struct B 
{
    void id() { cout << "B ";}
};

template<class A, class... As>
struct C : public A, public As... 
{
    void id()
    {
         A::id();
         As...::id(); // This line does not work, it is illustrative.
    }
};

int main()
{
    C<A, B> c;
    c.id();

    //expected: result A B 
}

问题是:是否可以扩展为...以某种方式在不使用递归方法的情况下执行此操作,只使用...运算符?

1 个答案:

答案 0 :(得分:27)

不确定。您需要一个允许包扩展的上下文 - 一个简单的上下文是一个支撑初始化列表,它也有保证从左到右评估的好处:

using expander = int[];
(void) expander { 0, ((void) As::id(), 0)... };
  • ...将图案展开到左侧;在这种情况下,模式是表达式((void) As::id(), 0)

  • 表达式中的,是逗号运算符,它计算第一个操作数,丢弃结果,然后计算第二个操作数,并返回结果。

  • (void)上的As::id()广告素材存在以防止重载operator,,如果您确定As::id()个调用都不会返回超载的内容,则可以省略逗号运算符。
  • 逗号运算符右侧的
  • 0是因为expanderint的数组,所以整个表达式(用于初始化数组的元素) )必须评估为int
  • 第一个0确保当As为空包时,我们不会尝试创建非法的0大小数组。

Demo

在C ++ 17中(如果幸运的话),C::id的整个主体可以替换为a binary fold expression(A::id(), ... , (void) As::id()); Demo