First of all the bad news. Unfortunately I have to say that the outcome of the standards meeting has been disappointing. Let me explain myself. We had a really very intense week here in Jacsonville. We made progress in many things, but we will not have the killer features I really wanted for C++17.
Not for C++17
Let me start enumerating the things we are not going to have in C++17:
- Modules: They will not be in C++17, but in a separate technical specification. That is really a pity, as we will be for more years without having what I was expecting to let me get rid of the text substitution paradigm of the include directive as well as improve compilation times.
- Concepts: They will not be integrated in C++17, but stay for now in their own technical specification. There were concerns about some issues in the TS, which I do not consider essential. Despite that there is implementation experience and even other TS depends on it, it was rejected to move concepts to the IS.
- Ranges: This is a library solution highly depending on concepts. So, no way.
- Coroutines: There was a very sound proposal with implementation experience. However, again this was targeted to another technical specification.
Besides that, we also will not have the half of uniform function call syntax that I thought was not controversial. To be clear, I really wanted both halves.
A different case is contracts, where I think we made progress an achieving a unified proposal, however this needs more work before being able to target C++ (either a TS or the next release of C++ after C++17).
Some good news
Still there are good news in terms of major features. We decided to incorporate to C++17 the technical specification for parallelism. That is something! It means that we will be able to use parallel versions of many standard library algorithms and getting the performance of their parallel and vectorized versions.
C++17 will include what was a separate international standard (ISO/IEC 29124:2010). Consequently you will be able to rely on any C++17 implementation to make use of functions like Bessel functions, Legendre polynomials, Hermite polynomials or Riemann zeta function.
Finally, C++17 will also include the file system technical specification providing a modern library solution to access to the file system.
C++17 major or minor
All in one this means that C++17 is seen by me as a minor update. I agree we will have some nice features. But I also think that we have set a very high threshold for setting new things in the C++ standard. Some people seriously questioned features that had been used for a decade with multiple implementations.
If C++ had used this threshold in the past we would probably had delayed many of the features that today are part of C++ (templates and STL come to my mind).
Some may say that we could see that C++17 plus the accompanying technical specifications make a good set and users will be able to use all of them. However, for many organizations TS are seen as experimental features they are not willing to use in production code. And they are sometimes right.
Some accepted features
As I do not want to be so negative, let me tell what was approved to be in C++17.
Fall-through in switch statements
You now may explicitly say that a case branch wants to fall-through and that you did not forget the break:
switch (n) { case 1: case 2: f1(); [[fallthrough]]; case 3: // Hopefully no warning f2(); break; case 4: // No warning f3(); case 5: // Hopefully warn on fallthrough f4(); [[fallthrough]]; // illformed }
Avoiding discarding results
You may want to issue a warning when you do not use the result of a function.
struct [[nodiscard]] mytype { /*...*/ }; mytype f(); [[nodiscard]] int g(); void g() { f(); // warning: result discarded auto x = f(); // OK. f(); // warning: result discarded auto x = g(); // OK. }
Unused value
Unused warnings are useful but sometimes you really want to avoid them
void f() { int x; // warning: unused int y; // warning in non-deubg mode assert(y==0); } void g() { [[maybe_unused]] int x; // avoid warning [[maybe_unused]] int y; // avoid warning assert(y==0); }
Extending aggregate initialization
Aggregate initialization has been extended to support inheritance avoiding the need to define boilerplate constructors.
struct A { int x; }; struct B : A { double y; }; void f() { A a{2}; B b { {1}, 2}; // b.x=1, b.y=2 B b { {}, 2}; // b.x=0, b.y=2 }
Constexpr lambdas
Lambda expressions can be used on constexpr contexts:
constexpr int f(int x) { return [x] { return x+10; } }
This also means that now a lambda expression can be constexpr itself. However, if the contexpr specifier is omitted when it satisifes requrements for constexpr.
auto f = [](int x) constexpr { return x; } auto g = [](int x) { return x; } constexpr z1 = f(2); constexpr z2 = g(2); int y = get_value(); constexpr z3 = f(y); // Ouch! constexpr z4 = g(y); // Argh!
Removing unary folds
Unary folds have defaults in empty parameter packs. Some of them (*, +, &, |) have been removed as they could lead to surprising results.
template <typename ... Args> auto sum1(Args ... args) { return (args+...); // Ill formed: No default } template <typename ... Args> auto sum2(Args ... args) { return (args+...+0); // OK: Default provided }
Generalized for range loop
Range based for loops may now have different types for begin and end.
{ auto && __range = for-range-initializer; auto __begin = begin-expr; auto __end = end-expr; for ( ; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } }
Capturing *this by value
A new capture for this has been added to lambda expressions. While capturing this, captures the this pointer, *this captures by copy.
auto f = [*this]() { /* ... */ } // captures by copy
Construction rules for enum values
We now allow implicit/non-narrowing conversions from a scoped enumeration’s underlying type to the enumeration itself, when its definition introduces no enumerator and the source uses a list-initialization syntax. This is safe and support very useful programming techniques. For example, you could introduce new integer types that enjoy the same existing calling conventions as its underlying integer type.
enum class my_integer : int {}; struct A { my_integer value; }; A a = { my_integer{17} }; void f(my_integer}; void g() { f(my_integer{24}); // ... }
Hexadecimal floating point literals
Now you can express floating point literals in hexadecimal.
float x = 0xC.68p+2; // 49.625
Asking about lock-free at compile time
As a complement of is_lock_free() a static constexpr counterpart is now provided. Now atomic types have a new static constexpr bool named is_always_lock_free
static_assert(!atomic<T>::is_always_lock_free, "No lock free");
This is also useful to be used in enable_if contexts.
New traits for swappable and nothrow-swappable
New traits have been added and used for swap functions.
- is_swappable_with<T,U>
- is_swappable<T>
- is_nothrow_swappable_with<T,U>
- is_nothrow_swappable<T>
Clamping values
Function clamp ensures to make a value fit withing two bounds.
int x 10; int y1 = clamp(x,5,8); // y1=8 int y2 = clamp(x,15,20); // y2=15 int y3 = clamp(x,5,15); // y3 = 10
Other library improvements
- A better and more precise specification for enable_shared_from_this.
- The addition of predicate not_fn from the library fundamentals TS.
- Changes to searchers interfaces.
- A portable way of accessing cache line sizes through hardware_destructive_interference_size and hardware_constructive_interference_size.
- A new overload to std::hypot to be able to to take 3 arguments which makes easy to compute distance between 2 3D points.
- A number of constexpr additions to iterators.
- A non const version of data member function of std::string.
- Additional traits std::is_callable and std::is_nothrow_callable.