2018-09-10
As mentioned in earlier posts, I attended ACCU 2018 but the day before there are some pre-conference sessions to get a more indepth understand of a particular topic. I’ve spend a day with Raspberry Pi’s the first conference I did.
This time I attended a day with Nicolai Josuttis entitled “Modern C++ (C++ 17)”.
This blog post attempts to cover all the things I discovered on that day along with other relevant information, distilled, mostly to act as a prompt for myself and others about what is available.
WARNING : This post is incomplete … this is a big subject
Overview of current status of C++ standardisation
In this section I wil highlight the new things that came along in C++ 17.
In my view these really help developers to add descriptions to code flow, they are syntactic sugar really, essentially an alias for a potentially far more complex abstraction, but they don’t have to be references.
Where you might have had this (or far worse) …
const auto& result = someMap.insert(somePair(someKey, someValue));
const auto& keyIterator = result.first;
const auto& wasInserted = result.second;
if (wasInserted) { ... }
… we can now write this …
const auto& [keyIterator, wasInserted] = someMap.insert(somePair(someKey, someValue));
if (wasInserted) { ... }
These bindings latch on to anything that has structure with a corresponding number of data members, like class/struct, std::pair, std::tuple and std::array types. If there is a mismatch between the number of elements on either side of the ‘=’ then a compiler error ensues.
An extremely convenient construct for iterating dictionaries is …
for (const auto& [key,value] : someDictionary) { ... }
Structured Bindings on CppReference
So you are probably used to doing this …
for (auto i = 0; i < 10 ; i++) {...}
… you can now do this in if statements (with the help of an optional init-statement …
if (auto i = someFunc(); i < 10)
{ /* i in scope here */ }
else if (i > 90)
{ /* and here */ }
else
{ /* and here */ }
// but not here
… and switch statements …
switch (auto someThing = someFunc(); someThing.Level())
{
case One:
{
/* someThing is in scope here */
break;
}
...
}
// but not here
… but you cannot do this …
while (auto someThing = someFunc(); someThing.continue()) // error
{...}
… ditto for do {…} while.
How could this have taken so long … this is how it is defined …
enum class byte : unsigned char {} ;
… and can be used as follows …
#include <cstddef>
std::byte b { 0xff };
std::variant<> is a new template that can be exactly one object of a fixed type from a set of [non-unique] type alternatives.
This also describes a type-safe union.
std::variant<std::byte, int, std::string> defaultVariant;
// var is actually holding a byte (the first type specified) with byte's default value
std::variant<std::byte, int, std::string> stringVariant{"Hello world"};
It is possible to make a std::variant hold nothing by using std::monostate as an alternative type … this is a special structure designed explicitly for the purpose as it is empty and well behaved (unlike void).
Variants can be used to eliminate run time polymorphism, in some situations this is desirable for performance reasons.
std::variant<> std::monostate static polymorphism
std::optional<> is a class that can store exactly zero or one object of a single type.
std::optional<std::string> emptyString;
std::optional<std::string> nonEmptyString{"Hello world"};
bool negative = emptyString.has_value();
bool positive = nonEmptyString.has_value();
statics in headers
Derived d{ {base1, base2}, derived3 }
namespace A::B::C {…}
Instead of the extremely painful and totally unnecessary (in C++11) …
std::lock_guard<std::recursive_mutex> lock(mutex);
… in C++17 we can now write …
std::lock_guard lock(mutex);
… phew … I always wondered why I needed to repeat myself … and also it allowed more scope for compilation errors … I love this change.
using Base::A, Base::B;
In this section I will highlight some of the things that were deprecated
#