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

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:

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:

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

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

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

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

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

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

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