A u8 character literal
In C++17 was added new character literal for UTF-8.
For example:
1 2 3 4 5 |
int main() { auto c = u8'a'; return 0; } |
It’s used for correct translation characters to ASCII on different platforms.
Hexadecimal floating point literals
In C++17 was added hexadecimal floating point literals.
1 |
double d = 0x1.2p3; // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0 |
Link for cppreference.
template<auto>
Allows avoiding type name in templates. For example, in C++14 it was:
1 2 3 4 5 6 |
template<typename Type, Type x> constexpr auto value = x; int main() { auto x = value<int,3>; return 0; } |
and in C++17 it looks like this:
1 2 3 4 5 6 7 8 |
template<auto x> constexpr auto value = x; int main() { auto x = value<3>; return 0; } |
Type of x will be deduced by a compiler.
Also, it’s possible to use auto with variadic templates:
1 2 3 4 5 6 7 |
template<auto ... values> struct A {}; int main() { auto k = A<1, 2, 'c'>(); return 0; } |
class template argument deduction
In C++17 template parameters can be deduced by a compiler. For example in C++14:
1 |
std::vector<int> vector = { 1, 2, 3, 4, 5 }; |
and in C++17 it will be:
1 |
std::vector vector = { 1, 2, 3, 4, 5 }; |
The same rule works with operator new:
1 2 3 4 5 6 7 8 9 10 |
template<class T> struct A{ A(T, T) {} }; int main() { auto pointer = new A{1, 2}; return 0; } |
constexpr if
constexpr if allows check conditions on compile time. For example:
1 2 3 4 5 6 |
template<typename ... Args> auto printer(Args ... args) { if constexpr (sizeof...(args) > 0) return "Some Values"; return "None"; } |
or:
1 2 3 4 5 6 7 |
template <typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) return *t; // deduces return type to int for T = int* else return t; // deduces return type to int for T = int } |
selection statements with initializer
In C++17 there are new versions of if and switch statements:
- if ( init; condition)
- switch(init; condition)
It was added because there is a lot of cases where a variable is created only to use inside some if or switch statement.
For example before C++17:
1 2 3 4 5 6 7 8 |
{ auto p = m.try_emplace(key, value); if (!p.second) { FATAL("Element already registered"); } else { process(p.second); } } |
and on C++17:
1 2 3 4 5 |
if (auto p = m.try_emplace(key, value); !p.second) { FATAL("Element already registered"); } else { process(p.second); } |
Switch statement C++14:
1 2 3 4 5 6 7 |
{ Foo gadget(args); switch (auto s = gadget.status()) { case OK: gadget.zip(); break; case Bad: throw BadFoo(s.message()); } } |
switch C++17:
1 2 3 4 |
switch (Foo gadget(args); auto s = gadget.status()) { case OK: gadget.zip(); break; case Bad: throw BadFoo(s.message()); } |
constexpr lambdas
In C++17 was added ability create lambda as constexpr.
For example:
1 2 3 4 5 6 7 8 9 10 11 |
constexpr auto DoJob(int x) { return [x]() { return x + 1; }; } int main() { constexpr auto lambda = []() {}; DoJob(2); return 0; } |
lambda capture of *this
In C++17 now it’s possible to pass *this to lambda.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A { private: int x{ 10 }; public: auto DoJob() { return [*this]() { std::cout << x << std::endl; }; } }; int main() { A a; a.DoJob()(); return 0; } |
Lambda expressions declared within a non-static member function explicilty or implicitly captures the this pointer to access to member variables of this. Both capture-by-reference [&] and capture-by-value [=] capture-defaults implicitly capture the this pointer, therefore member variables are always accessed by reference via this. Thus the capture-default has no effect on the capture of this. Asynchronous dispatch of closures is a cornerstone of parallelism and concurrency. When a lambda is asynchronously dispatched from within a non-static member function, via std::async or other concurrency / parallelism dispatch mechanism, the *this object cannot be captured by value. Thus when the std::future (or other handle) to the dispatched lambda outlives the originating class the lambda’s captured this pointer is invalid.
There is a workaround on C++14:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A { private: int x{ 10 }; public: std::future<int> DoJob() { return std::async(std::launch::async,[=, tmp = *this]()->int { std::cout << tmp.x << std::endl; return tmp.x; }); } }; int main() { A a; a.DoJob(); return 0; } |
This workaround has two liabilities. First, this pointer is also captured which provides a significant opportunity to erroneously reference a this->member instead of a tmp.member as there are two distinct objects in the closure that reference two distinct members of the same name. Second, it is onerous and counter-productive to the introduction of asynchronously dispatched lambda expressions within existing code. Consider the case of replacing a for loop within a non-static member function with a parallel for each construct as in the parallelism technical specification.