Last Update:
C++17 in details: Templates
Table of Contents
For C++17 everyone wanted to have concepts, and as you know, we didn’t get them. But does it mean C++17 doesn’t improve templates/template meta-programming? Far from that! In my opinion, we get excellent features.
Read more for details.
Intro
Do you work a lot with templates and meta-programming?
With C++17 we get a few nice improvements: some are quite small, but
also there are notable features as well! All in all, the additions
should significantly improve writing template code.
Today I wrote about:
- Template argument deduction for class templates
template<auto>
- Fold expressions
constexpr if
- Plus some smaller, detailed improvements/fixes
BTW: if you’re really brave you can still use concepts! They are merged into GCC so you can play with them even before they are finally published.
The Series
This post is the third one in the series about C++17 features details.
The plan for the series
- Fixes and deprecation
- Language clarification
- Templates (today)
- Attributes
- Simplification
- Library changes - Filesystem
- Library changes - Parallel Algorithms
- Library changes - Utils
- Wrap up, Bonus - with a free ebook! :)
Documents & Links
Just to recall:
First of all, if you want to dig into the standard on your own, you can read the latest draft here:
N4659, 2017-03-21, Working Draft, Standard for Programming Language C++
- the link also appears on the isocpp.org.
WG21 P0636r0: Changes between C++14 and C++17
Compiler support: C++ compiler support
Moreover, I’ve prepared a list of concise descriptions of all of the C++17 language features:
Download a free copy of my C++17 Cheat Sheet!
It’s a one-page reference card, PDF.
There’s also a talk from Bryce Lelbach: C++Now 2017: C++17 Features
And have a look at my master C++17 features post: C++17 Features
Template argument deduction for class templates
I have good and bad news for you :)
Do you often use make<T>
functions to construct a templated object
(like std::make_pair
)?
With C++17 you can forget about (most of) them and just use a regular
constructor :)
That also means that a lot of your code - those make<T>
functions can
now be removed.
The reason?
C++17 filled a gap in the deduction rules for templates. Now the template deduction can happen for standard class templates and not just for functions.
For instance, the following code is (and was) legal:
void f(std::pair<int, char>);
// call:
f(std::make_pair(42, 'z'));
Because std::make_pair
is a template function (so we can perform
template deduction).
But the following wasn’t (before C++17)
void f(std::pair<int, char>);
// call:
f(std::pair(42, 'z'));
Looks the same, right? This was not OK because std::pair
is a template
class, and template classes could not apply type deduction in their
initialization.
But now we can do that so that the above code will compile under C++17 conformant compiler.
What about creating local variables like tuples or pairs?
std::pair<int, double> p(10, 0.0);
// same as
std::pair p(10, 0.0); // deduced automatically!
Try in Compiler Explorer: example, GCC 7.1.
This can substantially reduce complex constructions like
std::lock_guard<std::shared_timed_mutex,
std::shared_lock<std::shared_timed_mutex>> lck(mut_, r1);
Can now become:
std::lock_guard lck(mut_, r1);
Note, that partial deduction cannot happen, you have to specify all the template parameters or none:
std::tuple t(1, 2, 3); // OK: deduction
std::tuple<int,int,int> t(1, 2, 3); // OK: all arguments are provided
std::tuple<int> t(1, 2, 3); // Error: partial deduction
Also if you’re adventurous you can create your custom class template deduction guides: see here for more information: recent post: Arne Mertz: Modern C++ Features - Class Template Argument Deduction.
BTW: why not all make
functions can be removed? For example, consider
make_unique
or make_shared
are they only for ‘syntactic sugar’? Or
they have other important uses? I’ll leave this as an exercise :)
More details in
- P0091R3
- Simon Brand: Template argument deduction for class template constructors
- Class template deduction(since C++17) - cppreference.
MSVC not yet, GCC: 7.0, Clang: not yet.
Declaring non-type template parameters with auto
This is another part of the strategy to use auto
everywhere. With
C++11 and C++14 you can use it to automatically deduce variables or even
return types, plus there are also generic lambdas. Now you can also use
it for deducing non-type template parameters.
For example:
template <auto value> void f() { }
f<10>(); // deduces int
This is useful, as you don’t have to have a separate parameter for the type of non-type parameter. Like:
template <typename Type, Type value> constexpr Type TConstant = value;
// ^^^^ ^^^^
constexpr auto const MySuperConst = TConstant<int, 100>;
with C++17 it’s a bit simpler:
template <auto value> constexpr auto TConstant = value;
// ^^^^
constexpr auto const MySuperConst = TConstant <100>;
So no need to write Type
explicitly.
As one of the advanced uses a lot of papers/blogs/talks point to an example of Heterogeneous compile time list:
template <auto ... vs> struct HeterogenousValueList {};
using MyList = HeterogenousValueList<'a', 100, 'b'>;
Before C++17 it was not possible to declare such list directly, some wrapper class would have to be provided first.
More details in
- P0127R2 - Declaring non-type template parameters with auto
- P0127R1 - Declaring non-type template arguments with auto - motivation, examples, discussion.
- c++ - Advantages of auto in template parameters in C++17 - Stack Overflow
- Trip report: Summer ISO C++ standards meeting (Oulu) | Sutter’s Mill
MSVC not yet, GCC: 7.0, Clang: 4.0.
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 + ...);
}
Fold expressions over a parameter pack.
Also by default we get the following values for empty parameter packs (P0036R0):
Here’s quite nice implementation of a printf
using folds:
template<typename ...Args>
void FoldPrint(Args&&... args) {
(cout << ... << forward<Args>(args)) << '\n';
}
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), ...);
}
In general, fold expression allows writing cleaner, shorter and probably easier to read code.
More details in:
- N4295 and P0036R0
- “Using fold expressions to simplify variadic function templates” in Modern C++ Programming Cookbook.
- Simon Brand: Exploding tuples with fold expressions
- Baptiste Wicht: C++17 Fold Expressions
- Fold Expressions - ModernesCpp.com
MSVC not yet, GCC: 6.0, Clang: 3.6 (N4295)/3.9(P0036R0).
constexpr if
This is a big one!
The static-if for C++!
The feature allows you to discard branches of an if statement at compile-time based on a constant expression condition.
if constexpr(cond)
statement1; // Discarded if cond is false
else
statement2; // Discarded if cond is true
For example:
template <typename T>
auto get_value(T t) {
if constexpr (std::is_pointer_v<T>)
return *t;
else
return t;
}
This removes a lot of the necessity for tag dispatching and SFINAE and
even for #ifdefs
.
I’d like to return to this feature when we are discussing features of
C++17 that simplify the language. I hope to come back with more examples
of constexpr if
.
More details in:
MSVC 2017, GCC: 7.0, Clang: 3.9.
Other
In C++17 there are also other language features related to templates. In this post, I wanted to focus on biggest enhancements, so I’ll just mention the other briefly:
-
Allow
typename
in a template template parameters: N4051.- Allows you to use
typename
instead ofclass
when declaring a template template parameter. Normal type parameters can use them interchangeably, but template template parameters were restricted toclass
.
- Allows you to use
-
DR: Matching of template template-arguments excludes compatible templates: P0522R0.
- Improves matching of template template arguments. Resolves Core issue CWG 150.
-
Allow constant evaluation for all non-type template arguments: N4268
- Remove syntactic restrictions for pointers, references, and pointers to members that appear as non-type template parameters:
-
constexpr
lambdas: P0170R1- Lambda expressions may now be constant expressions.
Summary
Is C++17 improving templates and meta-programming? Definitely!
We have really solid features like template deduction for class
templates, template<auto>
plus some detailed features that fix some of
the problems.
Still, for me, the most powerful features, that might have a significant
impact on the code is constexpr if
and folds. They greatly clean up
the code and make it more readable.
What are your favorite parts regarding templates?
Next time we’ll address attributes like [[fallthrough]]
or
[[nodiscard]]
, and I’d like to recall other, already existing
attributes. Stay tuned!
Once again, remember to grab my C++17 Language Ref Card .
I've prepared a valuable bonus for you!
Learn all major features of recent C++ Standards on my Reference Cards!
Check it out here: