3

我目前正在玩模板元編程。我試圖通過使用tmp來創建一個有限狀態機。我知道網絡中有幾種實現方式,但我想自己實現一種實踐方式。C++展開可變參數模板參數到一個語句

我有一個叫做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); 
    } 

}; 

這裏的問題是,編譯器將擴​​大這個rec​​ursivly,這導致了很多在運行時由於input參數遞歸調用。它應該更有效率,如果擴展的代碼將是一個像這樣的陳述:

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

這是可能以某種方式?

+0

你可以使用的va_list通過它們使用循環並檢查每個comp值? – Abhinav

+2

我現在無法測試它,但是您確定編譯器在使用優化進行編譯時仍然使用遞歸調用嗎? –

+0

我不認爲*編譯器會以遞歸方式擴展*,但它會在編譯時將其擴展爲您想要的代碼。你測試過了嗎? – Walter

回答

4

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年:
CppCon 2015: Vittorio Romeo 「for_each_argument explained and expanded"

+1

謝謝!和一個偉大的談話! Unforunatly VS 2017 RC現在不支持摺疊表達式,所以我不得不使用C++ 11解決方案。我必須對'for_each_argument'函數做一些修改,因爲你的版本不是C++編譯器支持的(根據錯誤C4576)。我將其替換爲您在演講中建議的初始化程序列表:'std :: initializer_list {(f(std :: forward (xs)),0)...};'。 – Timo

1

有美好的舊把戲逗號

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

小心擴展逗號技巧?這只是爲了引入一個序列點嗎? –

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