【问题标题】:C++ expand variadic template arguments into a statementC++ 将可变参数模板参数扩展为语句
【发布时间】:2017-05-28 03:57:40
【问题描述】:

我目前正在玩模板元编程。我正在尝试使用 tmp 制作一个有限状态机。我知道网络上有几种实现,但我想自己实现一个作为练习。

我有一个名为Condition 的类,它是两个状态之间转换条件的基类。一种实现是AnyCondition 类:

template<class Input, Input comp, Input ... comps >
class AnyCondition: public Condition<Input>
{    
public:
    AnyCondition() {}

    bool operator()(const Input& input) const override
    {
        return input == comp || AnyCondition<Input, comps...>()(input);
    }

};

这里的问题是,编译器将递归地扩展它,由于input 参数,这会在运行时导致大量递归调用。如果扩展代码是这样的语句,它应该更有效:

    bool operator()(const Input& input) const override
    {
        return input == comp1 || input == comp2 || input == comp3...
    }

这有可能吗?

【问题讨论】:

  • 您可以使用 va_list 循环遍历它们并检查每个 comp 值吗?
  • 我现在无法测试,但你确定编译器在优化编译时仍然使用递归调用吗?
  • 我不认为编译器会递归地扩展它,但它会在编译时将它扩展成你想要的代码。你测试过吗?
  • @Abhinav va_list 不是模板元编程。 OP 正在寻找具有类型安全性的编译时解决方案。
  • 啊,好的,谢谢!保罗

标签: c++ c++11 templates variadic-templates template-meta-programming


【解决方案1】:

C++17 解决方案 - fold expression:

template <typename... Ts>
auto anyCondition(Ts... xs)
{
    return (xs || ...);
}

wandbox example


C++11 解决方案 - for_each_argument:

template <typename TF, typename... Ts>
void for_each_argument(TF&& f, Ts&&... xs)
{
    (void)(int[]){(f(std::forward<Ts>(xs)), 0)...};
}

template <typename... Ts>
auto anyCondition(Ts... xs)
{
    bool acc = false;
    for_each_argument([&acc](bool x){ acc = acc || x; }, xs...);
    return acc;
}   

wandbox example

我在 CppCon 2015 上发表了关于这个 sn-p 的演讲:
CppCon 2015: Vittorio Romeo “for_each_argument explained and expanded"

【讨论】:

  • 谢谢!和一个伟大的谈话!不幸的是 VS 2017 RC 现在不支持折叠表达式,所以我不得不使用 C++11 解决方案。我不得不对for_each_argument 函数进行一些更改,因为C++ 编译器不支持您的版本(根据错误C4576)。我将其替换为您在演讲中建议的初始化列表:std::initializer_list&lt;int&gt;{(f(std::forward&lt;Ts&gt;(xs)), 0)... };
【解决方案2】:

我很确定任何体面的编译器都会将递归优化到循环中。但是,如果您正在寻找一些额外的方法来扩展单一类型的可变参数列表,您可以使用简单的std::initializer_list 技巧:

constexpr auto list = {comp, comps...};

或者,在你的情况下:

inline bool operator()(const Input &input) const override {
    bool res = false;
    for (auto val : {comp, comps...})
        res |= val == input;
    return res;
}

【讨论】:

    【解决方案3】:

    有一个很好的旧逗号技巧

    bool operator()(const Input& input) const override
    {
      bool ret { input == comp };
    
      int  unusedA [] { ( ret |= (input == comps), 0 ) ... };
    
      (void)unusedA; // to avoid the unused warning
    
      return ret;
    }
    

    【讨论】:

    • 想扩展逗号技巧?是不是只是为了引入一个序列点?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    • 1970-01-01
    • 1970-01-01
    • 2015-05-21
    • 2014-10-30
    相关资源
    最近更新 更多