Home » Advertising » C++17 in Detail: Templates

C++17 in Detail: Templates

Introduction

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.
  • templateauto
  • 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.

Documents and Links

First of all, if you want to dig into the standards 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 makeT 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 makeT 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::pairint, 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::pairint, 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 the under C++17 conformant compiler.

What about creating local variables like tuples or pairs?

std::pairint, double p(10, 0.0);
// same as
std::pair p(10, 0.0); // deduced automatically!

Try it in Compiler Explorer: example, GCC 7.1.

This can substantially reduce complex constructions like:

std::lock_guardstd::shared_timed_mutex, 
        std::shared_lockstd::shared_timed_mutex lck(mut_, r1);

The above 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::tupleint,int,int t(1, 2, 3); // OK: all arguments are provided
std::tupleint 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 by Arne Mertz, Modern C++ Features – Class Template Argument Deduction.

By the way: why can’t all make functions be removed? For example, consider make_unique or make_shared. Are they only for ‘syntactic sugar’? Or do they have other important uses? I’ll leave this as an exercise.

More details in:

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() { }

f10();               // 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 = TConstantint, 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 lists:

template auto ... vs struct HeterogenousValueList {};
using MyList = HeterogenousValueList'a', 100, 'b';

Before C++17, it was not possible to declare such lists directly, some wrapper class would have to be provided first.

More details in:

MSVC not yet, GCC: 7.0, Clang: 4.0.

Fold Expressions

With C++11, we got variadic templates which are 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;
}

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

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

templatetypename ...Args auto sum(Args ...args) 
{ 
    return (args + ... + 0); 
}

// or even:

templatetypename ...Args auto sum2(Args ...args) 
{ 
    return (args + ...);
}

The following are fold expressions over a parameter pack.

Also, by default, we get the following values for empty parameter packs (P0036R0):

Here’s a quite nice implementation of a printf using folds:

templatetypename ...Args
void FoldPrint(Args... args) {
    (cout  ...  forwardArgs(args))  'n';
}

Or a fold over a comma operator:

templatetypename T, typename... Args
void push_back_vec(std::vectorT v, Args... args)
{
    (v.push_back(args), ...);
}

In general, fold expressions allow you to write cleaner, shorter and probably easier to read code.

More details in:

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_vT)
        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 the biggest enhancements, so I’ll just mention the others briefly:

  • Allow typename in a template’s parameters: N4051.
    • Allows you to use typename instead of class when declaring a template parameter. Normal type parameters can use them interchangeably, but template parameters were restricted to class.
  • DR: Matching of template-arguments excludes compatible templates: P0522R0.
  • Allows constant evaluation for all non-type template arguments: N4268.
    • Removes 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 there improved templates and meta-programming in C++17? Definitely!

We have really solid features like template deduction for class templates, templateauto, 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 areconstexpr 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!

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

cover letter