C++折疊表達式(Fold Expression)


折疊表達式(Fold Expression)

折疊表達式是C++17新引進的語法特性。使用折疊表達式可以簡化對C++11中引入的參數包的處理,從而在某些情況下避免使用遞歸。

 

支持的操作符

折疊表達式支持 32 個操作符:

+, -, *, /, %, ^, &, |, =, <,

>, <<, >>, +=, -=, *=, /=, %=, ^=, &=, |=, <<=,

>>=,==, !=, <=, >=, &&, ||, ,, .*, ->*.

 

語法形式

折疊表達式共有四種語法形式。分別為一元的左折疊和右折疊,以及二元的左折疊和右折疊。

1、一元右折疊(unary right fold)
  ( pack op ... )
  一元右折疊(E op ...)展開之后變為 E1 op (... op (EN-1 op EN))
2、一元左折疊(unary left fold)
  ( ... op pack )
  一元左折疊(... op E)展開之后變為 ((E1 op E2) op ...) op EN
3、二元右折疊(binary right fold)
  ( pack op ... op init )
  二元右折疊(E op ... op I)展開之后變為 E1 op (... op (EN−1 op (EN op I)))
4、二元左折疊(binary left fold)
  ( init op ... op pack )

  二元左折疊(I op ... op E)展開之后變為 (((I op E1) op E2) op ...) op EN

語法形式中的op代表運算符,pack代表參數包,init代表初始值。
初始值在右邊的為右折疊,展開之后從右邊開始折疊。而初始值在左邊的為左折疊,展開之后從左邊開始折疊。
不指定初始值的為一元折疊表達式,而指定初始值的為二元折疊表達式。
當一元折疊表達式中的參數包為空時,只有三個運算符(&& || 以及逗號)有缺省值,其中&&的缺省值為true,||的缺省值為false,逗號的缺省值為void()。

下面看看具體應用

求和

假設我們想寫一個能接受一個及以上參數的求和函數,

 1 #include <iostream>
 2 using namespace std;
 3  
 4 template<typename First>  
 5 First sum1(First&& value)  
 6 {  
 7     return value;  
 8 }
 9  
10 template<typename First, typename Second, typename... Rest>  
11 First sum1(First&& first, Second&& second, Rest&&... rest)  
12 {  
13     return sum1(first + second, forward<Rest>(rest)...);  
14 }
15  
16 template<typename First, typename... Rest>
17 First sum2(First&& first, Rest&&... rest)
18 {
19     return (first + ... + rest);
20 }
21  
22 int main()
23 {
24     cout << sum1(1) << sum1(1, 2) << sum1(1, 2, 3) << endl; // 136
25     cout << sum2(1) << sum2(1, 2) << sum2(1, 2, 3) << endl; // 136
26 }
  • 在C++17之前,求和函數sum1的實現必須分成兩個部分。其中4到8行的sum1函數用於處理一個參數的情況。而10到14行的sum1函數用於處理兩個及以上參數的情況。當參數個數大於一個時,10到14行的sum1函數將前兩個參數相加,然后遞歸調用自身。當參數個數只有一個時,4到8行的sum1函數將此參數返回,完成求和。sum1(1, 2, 3) = sum1(1+2, 3) = sum1(3, 3) = sum1(3+3) = sum1(6) = 6

  • 而在C++17之后,由於有了折疊表達式這個新特性,求和函數sum2不再需要處理特殊情況,實現大為簡化。
    sum2(1, 2, 3) = (1 + ... + pack(2, 3)) = (1+2) + 3 = 6
    這里sum2的實現所采用的是二元左折疊。

 

“與”和“或”

 1 #include <iostream>
 2 using namespace std;
 3  
 4 template<typename... Args>
 5 bool all(Args... args) {return (... && args);}
 6 template<typename... Args>
 7 bool any(Args... args) {return (... || args);}
 8  
 9 int main() 
10 {
11     cout << boolalpha << all(true, false, true) << endl; // false
12     cout << boolalpha << any(true, false, true) << endl; // true
13     cout << boolalpha << all() << endl; // true
14     cout << boolalpha << any() << endl; // false
15 }
  •  在這里all和any函數分別實現了不特定多數布爾值的與和或的運算。這兩個函數的實現均采用了一元左折疊。

    all(true, false, true) = (... && pack(true, false, true)) = (true && false) && true = false
    any(true, false, true) = (... || pack(true, false, true)) = (true || false) || true = true
  • 當一元折疊表達式中的參數包為空時,&&的缺省值為true,而||的缺省值為false。
    all() = (... && pack()) = true
    any() = (... || pack()) = false

     

“打印”和“調用”

 1 #include <iostream>
 2 using namespace std;
 3  
 4 template<typename... Ts>
 5 void printAll(Ts&&... mXs)
 6 {
 7     (cout << ... << mXs) << endl;
 8 }
 9  
10 template<typename TF, typename... Ts>
11 void forArgs(TF&& mFn, Ts&&... mXs)
12 {
13     (mFn(mXs), ...);
14 }
15  
16 int main() 
17 {
18     printAll(3, 4.0, "5"); // 345
19     printAll(); // 空行
20     forArgs([](auto a){cout << a;}, 3, 4.0, "5"); // 345
21     forArgs([](auto a){cout << a;}); // 空操作
22 }
  •  printAll函數實現了對不特定多數值的打印輸出。該函數的實現采用了二元左折疊。
    printAll(3, 4.0, "5")
    = (cout << ... << pack(3, 4.0, "5")) << endl
    = ((cout << 3) << 4.0) << "5" << endl
    = 打印345並換行
  • 當二元折疊表達式的參數包為空時,其計算結果為該二元折疊表達式中所預設的初始值。
    printAll()
    = (cout << ... << pack()) << endl
    = cout << endl
    = 空行
  • forArgs函數實現了依次使用多個參數調用某個單參數函數的功能。該函數的實現采用了一元右折疊。
    forArgs([](auto a){cout << a;}, 3, 4.0, "5")
    = ([](auto a){cout << a;}(pack(3, 4.0, "5")), ...)
    = [](auto a){cout << a;}(3), ([](auto a){cout << a;}(4.0), ([](auto a){cout << a;}("5")))
    = 打印345
  • 當使用逗號的一元折疊表達式中的參數包為空時,其計算結果為標准規定的缺省值void()。
    forArgs([](auto a){cout << a;})
    = ([](auto a){cout << a;}(pack()), ...)
    = void()

     

 轉自:(19條消息) C++17嘗鮮:fold expression(折疊表達式)_zwvista的專欄-CSDN博客_fold





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM