Fold Expressions in C++ 17

Share the Article

Fold expressions is a new feature in C++ 17. This feature is related to variadic arguments of templates. Fold expressions enables the C++17 compiler to apply same binary operation to all the arguments of parameter pack. The compiler first applies the binary operation to first set of 2 arguments. After this it applies the result to third argument and so on.

For instance,

If (T . . . args) is the variadic argument of a template function, then the fold expression may look like as follows:

(... <binary_operation> args); //fold expression

The compiler shall unfold this expression and apply binary operation as follows:

( (args1 <binary_operation> args2) <binary_operation> args3) <binary_operation> args4 . . and so on

Please note, that it is mandatory to place the fold expression in brackets ” ( ) “

Basic Example of using fold expressions in C++ 17

In the following example, the function sum( ) takes variadic list of arguments. This function shall compute total sum of all the arguments using fold expressions.

In this example, the fold expression is => ( . . . + vals). The compiler shall unfold it like, (vals1 + vals2) + vals3 and so on.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void sum(T ... vals) { int total = (... + vals); //Fold expression cout << "Total = " << total << endl; } int main() { sum(1); // = (1) sum(1, 2); // = (1 + 2) sum(1, 2, 3); // = ((1 + 2) + 3) sum(1, 2, 3, 4); // = (((1 + 2) + 3) + 4) return 0; }

Output

Output of code using fold expressions in C++ 17

Generic version of same example

The above example is too raw and it does not make use of template type. The following example is the better version and we can use this to work with different types.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void sum(T ... vals) { cout << "Total = " << (... + vals) << endl;; } int main() { sum(1); sum(1, 2); sum(1, 2, 3); sum(1, 2, 3, 4); cout << endl; sum(1.1); sum(1.1, 2.2); sum(1.1, 2.2, 3.3); sum(1.1, 2.2, 3.3, 4.4); cout << endl; sum(std::string("A")); sum(std::string("A"), std::string("B")); sum(std::string("A"), std::string("B"), std::string("C")); sum(std::string("A"), std::string("B"), std::string("C"), std::string("D")); return 0; }

Output:

Output of code adding and concatenating items and strings using fold expressions.

Pre and Post syntax of operations in Fold expressions in C++ 17

The above example uses a syntax where ellipses occur on left-side of binary operation. This is “pre” syntax. However, it is possible to place the ellipses to right-hand side and this is “post” syntax. The compiler unfolds “post” syntax in reverse direction.

Pre-add syntax: (... + vals) => (((vals1 + vals2) + vals3) + vals4) Post-add syntax: (vals + ...) => (vals1 + (vals2 + (vals3 + vals4))

Although for doing operations, like, addition, both the syntax shall generate same result. However, in other calculations, like, subtraction, division, etc. the result shall vary in using “pre” and “post” syntax. This is because, the position of arguments matter in these operations.

The following example demonstrates subtraction in both syntax.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void diff_L2R(T ... vals) //Pre syntax { int diff = (... - vals); cout << "Difference = " << diff << endl; } template<typename ... T> void diff_R2L(T ... vals) //Post syntax { int diff = (vals - ...); cout << "Difference = " << diff << endl; } int main() { diff_L2R(10); // = (10) = 10 diff_L2R(10, 1); // = (10 - 1) = 9 diff_L2R(10, 1, 2);// = ((10 - 1) - 2) = 7 cout << endl; diff_R2L(10); // = (10) = 10 diff_R2L(10, 1); // = (10 - 1) = 9 diff_R2L(10, 1, 2);// = (10 - (1 - 2) = 11 return 0; }

Empty parameter list with fold expressions in C++17

In previous examples, that compiler can unfold the expressions according the to variadic arguments. However, in case, there are zero arguments, then compiler shall generate error. The variadic arguments should have atleast one argument to unfold the expression.

The following example demonstrates this.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void sum(T ... vals) { cout << "Total = " << (... + vals) << endl;; } int main() { sum(); //No arguments passed return 0; }

Output:

Compiler error when not passing any argument in parameter pack of variadic argument list.

Exception in case of empty argument list

We know that in above example, the compiler throws error while unfolding the expression over operator+. However, there are exceptions:

  • operator&& (logical AND)
  • operator|| (logical OR)

For a fold expression, it is completely valid to accept empty arguments in above 2 cases. The logical AND shall generate always a TRUE value, whereas, logical OR shall always generate a FALSE value.

The following example, demonstrates this behavior with both “pre” and “post” type operations.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void logical_ops(T ... vals) { cout << "AND_pre = " << ( ... && vals) << endl;; cout << "AND_post = " << (vals && ... ) << endl;; cout << "OR_pre = " << ( ... || vals) << endl;; cout << "OR_post = " << (vals || ... ) << endl;; } int main() { logical_ops(); //No arguments return 0; }

Output

Output of code using fold expressions with empty argument list.

Left and Right fold expressions

Apart from the exception case as explained with Logical AND & Logical OR, all other cases cause error with zero arguments.

Therefore, to ensure that compiler do not throw an error with zero arguments, we may use left or right fold. In these fold expressions, any specific value can become part of expression. This value can occur, either, in left or right of side of main fold expression. Generally, we may use number 0 to make such expressions.

The following examples uses value 0, to for binary fold expressions.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void sum(T ... vals) { //Left-fold expression cout << "Total1 = " << (0 + ... + vals) << endl; //Right-fold expression cout << "Total2 = " << (vals + ... + 0) << endl; } int main() { sum(); return 0; }

Output

Output of code using left and right fold expressions in C++ 17

Calling some specific function for all arguments in fold expression in C++ 17

The compiler can unfold the expression with function call for each and every argument. Therefore, an expression like, following unfolds like below.

Given fold-expression, with function call - add_two( ) (... + add_two(vals) ); This shall evaluate to: (((add_two(vals1) + add_two(vals2)) + add_two(vals3) );

The following example applies this concept.

#include <iostream> //main header using namespace std; //for namespace int add_two(int x) { return (x+2); } template<typename ... T> void print(T ... vals) { int final_sum = (... + add_two(vals) ); cout << final_sum << endl; } int main() { print(1, 2); // = (add_two(1) + add_two(2)) = 7 return 0; }

How to check if all the variadic arguments are equal

The following example uses a different signature for template function getting variadic arguments. It receives the first argument in specific parameter and rest of arguments in variadic arguments. Now, with the concepts learnt so far, the compiler can call the function “is_equal” with first argument and each individual argument.

The given expression: (... && is_equal(val, vals) ) Unfolds to: ( (is_equal(val, vals1) && is_equal(val, vals2)) && is_equal(val, vals3) )

Therefore, the final result shall be Logical AND of all the “is_equal” function calls.

#include <iostream> //main header using namespace std; //for namespace bool is_equal(int x, int y) { return (x == y); } template<typename T1, typename ... T> void checkIfEqual(T1 val, T ... vals) { bool final_bool = (... && is_equal(val, vals) ); final_bool? cout << "EQUAL" : cout << "NOT-EQUAL"; cout << endl; } int main() { checkIfEqual(1, 2); checkIfEqual(2, 2); checkIfEqual(2, 2, 4, 8, 9); checkIfEqual(1, 1, 1, 1, 1, 1, 1); return 0; }

Output

Output of code which is iterating through variadic arguments using fold expressions.

How to print all the variadic arguments using fold expression

The following example uses “left-fold” to print all arguments. The “cout” shall be the left-fold item and “operator<<” is binary operation.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void print(T ... vals) { (cout << ... << vals ) << endl; } int main() { print(1, 2, 3, 4, 5, 6); return 0; }

The above fold expression shall unfold as follows:

Fold expression: (cout << ... << vals ) Unfolded: (((cout << vals1) << vals2) << vals3) + vals4

Therefore, the output is

Output of code printing all the elements in variadic argument using fold expresssion in C++ 17

How to use TAB spaces between arguments

In the above example, there is no space or newline between the items being printed. For achieving this, the code need some additional handling.

Wrong method

We cannot use a fold expression as in following example. This is because, the fold expression is “(vals << . . . << ‘\t’)”.

Since, there is no “cout” inside this. Therefore, the compiler shall assume that ” << ” is left-shift and not stream insertion. Secondly, without a “cout” , the compiler shall use ASCII value of ‘\t’, which is 9 and this shall not treat this as TAB character.

Therefore, unfold it as follows:

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void print(T ... vals) { cout << ( vals << ... << '\t') ; } int main() { print(1); // Leads to => cout << (1 << 9); cout << endl; return 0; }

The program shall print value 512 in this case.

Correct Method-1 : Using Function Call with fold expressions in C++ 17

The first method calls function, putspace( ) for each variadic argument. This function firstly, prints a TAB character and then, returns back the same argument in fold expression.

#include <iostream> //main header using namespace std;//for namespace template<typename T> const T& putspace(T& i) { cout << '\t'; return i; } template<typename ... T> void print(T ... vals) { (cout << ... << putspace(vals) ) << endl ; } int main() { print(1, 2, 3, 4, 5, 6); return 0; }

Output

Output of code to print all variadic arguments with TAB character as separator.

Correct Method-2 : Using lambda expression

Another method is to use a lambda expression instead of a separate function. The following examples shows that lambda has exactly same code as it was in method-1.

#include <iostream> //main header using namespace std; //for namespace template<typename ... T> void print(T ... vals) { auto putnewline = [](const auto& i) -> const auto& { cout << endl; return i; }; (cout << ... << putnewline(vals) ) << endl ; } int main() { print(1, 2, 3, 4, 5, 6); return 0; }

Calling member functions with fold expressions

When there are multiple objects in the variadic argument list, the fold expression can call same method in each object. In this case, the comma-operator can do the trick. Please note, not only the member function, the comma-operator can call any regular function with this method.

The comma shall unfold expression as shown below:

Fold-expression: (..., func(args) ) Unfolds to: ((func(args1), func(args2) ), func(args3) . . and so on

The following example shall call member function “funda( )” with each object.

#include <iostream> //main header using namespace std; //for namespace class MFBase { public: virtual void funda() = 0; }; class MFDerived1 : public MFBase { public: void funda() { cout << "MF Derived #1" << endl; } }; class MFDerived2 : public MFBase { public: void funda() { cout << "MF Derived #2" << endl; } }; template<typename ... T> void print(T ... vals) { (... , vals->funda() ); } int main() { MFBase *b1 = new MFDerived1(); MFBase *b2 = new MFDerived2(); print(b1, b2, b1, b2, b1); return 0; }

Output

Output of code which is calling member functions on multiple objects using variadic argument list.

Main Funda: The fold expression is powerful technique to apply same operation to all arguments in parameter pack.

Related Topics:

Class Template Argument Deduction in C++17
What is a Tuple, a Pair and a Tie in C++
C++ Multithreading: Understanding Threads
What is Copy Elision, RVO & NRVO?
Lambda in C++11
Lambda in C++17
std::chrono in C++ 11
Thread Synchronization with Mutex
Template type deduction in functions
How std::forward( ) works?
How std::move() function works?
What is reference collapsing?

Share the Article

10,177 thoughts on “Fold Expressions in C++ 17

  1. Ruletka to rozgrywka kojarzona przede wszystkim z dużymi pieniędzmi i prestiżem. Jednak teraz nie jest to gra tylko dla wybrańców. Obecnie wystarczy mieć wyłącznie chęci, dostęp do Internetu i trochę wolnego czasu. Nie potrzeba wielkich funduszy ani wyjścia do ekskluzywnego salonu gier. Darmowa ruletka online dostępna jest w każdym kasynie internetowym oraz na naszej stronie. Nie musisz inwestować kroci ani przejmować się eleganckim strojem, wystarczy, że masz ochotę na dobrą zabawę! Oprócz tego ruletka za darmo online niesienie ze sobą masę korzyści: Krem z Wąkrotą Azjatycką i Różą! Gry hazardowe znane są praktycznie od początków cywilizacji. Bardzo popularne są gry karciane, lecz chyba największym zainteresowaniem cieszy się ruletka. Jej charakterystycznym, a zarazem najważniejszym elementem jest specjalne koło służące do losowania numerów. https://uprahp.com/community/profile/ignacioquinn476/ Warto wskoczyć na Internet i poszukać neutralnych stron, gdzie można znaleźć rankingi i recenzje legalnych kasyn po polsku. Tylko należy szukać takich stron, które nie działają celowo na korzyść jakiegoś kasyna. Powinny być bezstronne. Dzięki rankingom mamy możliwość porównywania legalnych kasyn online. Z kolei recenzje pozwalają nam na bliższe zapoznanie się z ofertą danego kasyna. Lista wymagań, którą należy spełnić, aby uzyskać licencję, jest długa i surowa. Nawet największe zagraniczne marki kasyn wolą wycofać się z polskiego rynku, zamiast zgadzać się na działanie pod niekorzystnymi warunkami. W efekcie w Polsce obowiązuje monopolistyczny system, a w sieci znajduje się tylko jedno kasyno, które może pochwalić się polską licencją hazardową. Trochę większą swobodą mogą pochwalić się bukmacherzy działający w sieci. Polscy miłośnicy zakładów sportowych mogą wybierać spośród kilku krajowych i zagranicznych marek.

  2. Hey very nice blog!! Man .. Excellent .. Amazing .. I’ll bookmark your web site and take the feeds also…I am happy to find a lot of useful information here in the post, we need develop more strategies in this regard, thanks for sharing. . . . . .

  3. I’m impressed, I must say. Actually not often do I encounter a blog that’s both educative and entertaining, and let me let you know, you might have hit the nail on the head. Your idea is excellent; the problem is something that not sufficient individuals are speaking intelligently about. I’m very completely happy that I stumbled across this in my seek for something referring to this.

  4. Angefangen hat alles mit dem ersten Geldspielautomaten 1889. Dieser hatte einen prägnanten Seitenarm, von dem der Begriff einarmiger Bandit herrührt. Seit dieser Zeit hat sich vieles getan. Gerade dadurch, dass die Spielautomaten den Weg ins Internet gefunden haben, kennt ihre Vielfalt kaum mehr Grenzen. In den online Spielotheken werden die Slots zumeist in klassische und Video Spielautomaten unterteilt. Die erste Kategorie hat einen Aufbau mit 3 Walzen, wie man es von den ersten einarmigen Banditen kennt. Die modernen Videoslots haben in der Regel 5 Walzen, was zu viel mehr Gewinnlinien und Gewinnkombinationen führt. Bei einigen Exoten unter den Automatenspielen finden sich sogar noch mehr Walzen und Rollen. Sie erhalten 15 Freispiele mit Multiplikatoren, eikä muilla kuin silmäätekevillä ollut mitään asiaa baccarat-pöydän ääreen. Möchten Sie sich einfach zurücklehnen und genießen, der mittlerweile PayPal als Zahlungsmethode anbietet und akzeptiert. Superseven bietet aufgrund seiner internationalen Ausrichtung ein umfangreiches Portfolio an Zahlungsdienstleistern, der sollte sich hier auf der Seite ausführlicher umsehen und sich den Artikel mit der Erklärung genau durchlesen. https://www.vanxuan.center/forums/profile/katrinzpo440931 Das Spielen von Slots und Casinospielen in unserem kostenlosen Spielebereich ist wirklich sehr einfach. Sie müssen keinen Download durchführen, nichts herunterladen oder irgendetwas installieren. Durchstöbern Sie einfach unsere Rangliste der besten Spiele oder verwenden Sie die Suchfunktion, um ein betreffendes Spiel zu finden, das Sie gerne spielen möchten. Wenn Sie dieses dann anklicken, wird das Spiel für Sie geladen und schon kann es los gehen. Drücken Sie dann einfach auf „Start\ oder „Spin\”