7 Features of C++17 that will simplify your code

fenbf
32.4K views

Open Source Your Knowledge, Become a Contributor

Technology knowledge has to be shared and made accessible for free. Join the movement.

Create Content

Fold expressions

With C++11 we got variadic templates which is a great feature, especially if you want to work with a variable number of input parameters to a function. For example, previously (pre C++11) you had to write several different versions of a function (like one for one parameter, another for two parameters, another for three params... ).

Still, variadic templates required some additional code when you wanted to implement 'recursive' functions like sum, all. You had to specify rules for the recursion:

For example:

auto SumCpp11(){
    return 0;
}

template<typename T1, typename... T>
auto SumCpp11(T1 s, T... ts){
    return s + SumCpp11(ts...);
}

And with C++17 we can write much simpler code:


template<typename ...Args> auto sum(Args ...args) 
{ 
    return (args + ... + 0); 
}

// or even:

template<typename ...Args> auto sum2(Args ...args) 
{ 
    return (args + ...);
}
Simple folds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>
using namespace std;
template<typename ...Args> auto sum(Args ...args)
{
return (args + ... + 0);
}
template<typename ...Args> auto sum2(Args ...args)
{
return (args + ...);
}
int main()
{
cout << sum(1, 2, 3, 4, 5, 6, 7) << "\n";
cout << sum2(1, 2, 3, 4, 5, 6, 7) << "\n";
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Fold expressions over a parameter pack.

ExpressionExpansion
(... op pack)((pack1 op pack2) op ...) op packN
(init op ... op pack)(((init op pack1) op pack2) op ...) op packN
(pack op ...)pack1 op (... op (packN-1 op packN))
(pack op ... op init)pack1 op (... op (packN-1 op (packN op init)))

Also by default we get the following values for empty parameter packs:

Operatordefault value
&&true
||false
,void()

Here's quite nice implementation of a printf using folds P0036R0:

template<typename ...Args>
void FoldPrint(Args&&... args) {
    (cout << ... << forward<Args>(args)) << '\n';
}
Print and folds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
using namespace std;
template<typename ...Args>
void FoldPrint(Args&&... args) {
(cout << ... << forward<Args>(args)) << '\n';
}
int main()
{
FoldPrint("hello", ", ", 10, ", ", 90.0);
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Or a fold over a comma operator:

template<typename T, typename... Args>
void push_back_vec(std::vector<T>& v, Args&&... args)
{
    (v.push_back(args), ...);
}
Push back and folds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>
#include <vector>
using namespace std;
template<typename T, typename... Args>
void FoldPushBack(vector<T>& v, Args&&... args)
{
(v.push_back(args), ...);
}
int main()
{
vector<int> v;
FoldPushBack(v, 1, 2, 3, 4);
for (auto &i : v)
cout << i << "\n";
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

In general, fold expression allows writing cleaner, shorter and probably easier to read code.

More details in:

MSVC not yet, GCC: 6.0, Clang: 3.6 (N4295)/3.9(P0036R0).

Open Source Your Knowledge: become a Contributor and help others learn. Create New Content