cpp17 and beyond
TRANSCRIPT
![Page 1: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/1.jpg)
C++17 and BeyondA Glimpse at the C++ of Tomorrow
Andreas Weis
Munich C++ User Group, October 2016
![Page 2: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/2.jpg)
Big Features and Small Features
Bjarne talked a lot about upcoming features in hisCppCon keynote 1
For Bjarne, a big feature is one that changes how we thinkabout programs.
C++11 was a release with big features (Lambdas, MoveSemantics, Variadic Templates, . . . ), C++14 had mostlysmall features.
The hope for C++17 was to again get big features. But itlooks like none of them will make it into the standard in time.
1https://www.youtube.com/watch?v=_wzc7a3McOs
![Page 3: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/3.jpg)
Outline
This talk tries to focus on the big features.
We will not discuss C++17 only, but also some of thefeatures that are currently specified as a TS.
Still, there’s lots of stuff that we won’t talk about.
For a complete list of what’s new in C++17, check outAlisdair Meredith’s CppCon talk C++17 in Breadth (NotDepth)2
Feel free to disagree on whether the features discussed herewill have a major impact.
2https://www.youtube.com/watch?v=22jIHfvelZk
![Page 4: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/4.jpg)
if constexpr
Basically a smarter #if
Goes back to proposal N3329 from 2012, heavily inspired byD’s static if (introduced there in 2005)3
Was held back because of concerns about overlap withConcepts and uncertainty about scoping
Re-proposed as if constexpr in 2016 (after Concepts Lite),with slightly different semantics
3see also Alexandrescu’s Going Native 2012 talk Static if I had a hammer
![Page 5: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/5.jpg)
Compile time branching is weird
template <class T>
struct is_awesome;
template <class T>
void compute_stuff ()
{
if(is_awesome <T>:: value) {
// awesome algorithm
// ...
} else {
// slightly less awesome algorithm
// ...
}
}
![Page 6: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/6.jpg)
Compile time branching is weird
Compiler will (insistently) warn that you are branching on acompile-time constant condition.
Both branches need to compile for all valid Ts.
Runtime branching does not cut it. What we really want isconditional compilation.
![Page 7: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/7.jpg)
SFINAE to the rescue!
template <class T>
std:: enable_if_t <is_awesome <T>::value >
compute_stuff ()
{
// awesome algorithm
// ...
}
template <class T>
std:: enable_if_t <! is_awesome <T>::value >
compute_stuff ()
{
// slightly less awesome algorithm
// ...
}
![Page 8: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/8.jpg)
SFINAE to the rescue?
Lots of boilerplate and code duplication.
Requires intimate knowledge of what many consider a weirdcorner case of C++ templates.
std::enable if makes stuff easier, but is still prone tobreaking in non-obious ways.
![Page 9: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/9.jpg)
if constexpr
template <class T>
void compute_stuff ()
{
if constexpr(is_awesome <T>:: value) {
// awesome algorithm
// ...
} else {
// slightly less awesome algorithm
// ...
}
}
The discarded branch is not instantiated by the compiler.Otherwise behaves like a regular if statement.
![Page 10: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/10.jpg)
if constexpr
This is less powerful than the initial static if idea, whichwas much closer to #if than to if.
No branching at global or namespace scope.
No layout changes.
if constexpr introduces new scope.
![Page 11: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/11.jpg)
Compiler support for if constexpr
Only gcc 7 and clang 3.9 support it already.
![Page 12: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/12.jpg)
if and switch with initializer
void safe_init () {
{
std:: lock_guard <std::mutex > lk(mx_);
if(v.empty ()) {
v.push_back(kInitialValue );
}
}
// ...
}
Need an additional pair of {}s for scoping lock
When refactoring, take care that the if cannot be separatedfrom the lock
![Page 13: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/13.jpg)
if and switch with initializer
void safe_init () {
if(std::lock_guard <std::mutex > lk(mx_);
v.empty ()) {
v.push_back(kInitialValue );
}
// ...
}
lk is scoped by the if.
![Page 14: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/14.jpg)
if and switch with initializer
if(auto p = m.try_emplace(key , value);
!p.second)
{
FATAL("Element already registered");
} else {
process(p.second );
}
Avoids polluting the enclosing namespace with identifiers.
![Page 15: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/15.jpg)
Compiler support for if and switch with initializer
Only gcc 7 and clang 3.9 support it already.
![Page 16: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/16.jpg)
Structured Bindings
std::tie is not as useful as it should be:
std::map <std::string , int > my_map;
auto const ret = my_map.insert ({"key", 42});
std::map <std::string , int >:: iterator it;
bool was_inserted;
std::tie(it , was_inserted) = ret;
We lose type deduction for the iterator type.
We lose the const qualifier.
Separate declaration often not feasible for non-trivial types.
![Page 17: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/17.jpg)
Structured Bindings - To the rescue!
std::map <std::string , int > my_map;
auto const [it , was_inserted] =
my_map.insert ({"key", 42});
![Page 18: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/18.jpg)
Structured Bindings - Destructuring
But wait, there’s more:
struct Foo {
int i;
float f;
std:: string s;
};
Foo f;
// ...
auto [natural , real , text] = f;
![Page 19: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/19.jpg)
Structured Bindings
Alright, cool. But how is this a major feature?
![Page 20: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/20.jpg)
Some preliminary boilerplate...
struct any_type {
template <class T>
constexpr operator T(); // non explicit
};
template <class T, class ... TArgs >
class is_braces_constructible;
// std:: true_type if we can construct
// a T from the TArgs
![Page 21: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/21.jpg)
Struct to tuple
template <class T> auto to_tuple(T&& object) {
using type = std::decay_t <T>;
if constexpr(is_braces_constructible
<type , any_type , any_type >{})
{
auto&& [p1, p2] = object;
return std:: make_tuple(p1 , p2);
} else if constexpr(is_braces_constructible
<type , any_type >{})
{
auto&& [p1] = object;
return std:: make_tuple(p1);
} else {
return std:: make_tuple ();
}
}
![Page 22: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/22.jpg)
Simple compile-time reflection
Full example athttps://www.reddit.com/r/cpp/comments/4yp7fv/c17_
structured_bindings_convert_struct_to_a_tuple/.
Once we have tuples, we can do all kinds ofmetaprogramming (see Boost Hana)
Not clear how much benefit structured bindings really bring,but chances are they will turn out more powerful than theyseem at first glance.
![Page 23: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/23.jpg)
Structured bindings + if with initializer
std::map <std::string , int > my_map;
if(auto const [it , was_inserted] =
my_map.insert ({"key", 42});
!was_inserted)
{
throw InsertionFailed ();
}
![Page 24: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/24.jpg)
Compiler support for structured bindings
Only (partially) supported in Clang 4.0 today.
![Page 25: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/25.jpg)
Concepts TS (not part of C++17)
Specify constraints for template arguments.
template <typename T>
T const& min(T const& a, T const& b)
{
return (a < b) ? a : b;
}
![Page 26: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/26.jpg)
Concepts Example
template <typename T>
concept bool LTComparable = requires(T a, T b)
{
{ a < b } -> bool;
};
template <LTComparable T>
T const& min(T const& a, T const& b)
{
return (a < b) ? a : b;
}
![Page 27: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/27.jpg)
Error messages with concepts
Here’s what happens if we try to use min with a type Foo thatdoes not have operator<:
In function ’int main()’:
error: cannot call function ’const T& min(const T&, const T&) [with T = Foo]’
std::cout << min(a, b) << std::endl;
^
note: constraints not satisfied
T const& min(T const& a, T const& b)
^~~
note: within ’template <class T> concept const bool LTComparable <T> [with T = Foo]’
concept bool LTComparable = requires(T a, T b) {
^~~~~~~~~~~~
note: with ’Foo a’
note: with ’Foo b’
note: the required expression ’(a < b)’ would be ill -formed
![Page 28: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/28.jpg)
Error messages with concepts
Here’s what happens if there is an operator<, but it does notreturn bool:In function ’int main()’:
error: cannot call function ’const T& min(const T&, const T&) [with T = Foo]’
std::cout << min(a, b) << std::endl;
^
note: constraints not satisfied
T const& min(T const& a, T const& b)
^~~
note: within ’template <class T> concept const bool LTComparable <T> [with T = Foo]’
concept bool LTComparable = requires(T a, T b) {
^~~~~~~~~~~~
note: with ’Foo a’
note: with ’Foo b’
note: ’operator <(a, b)’ is not implicitly convertible to ’bool ’
![Page 29: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/29.jpg)
Concepts
Concepts are not checked for completeness. Theimplementation may use a concept parameter like anytypename.
template <LTComparable T>
T const& max(T const& a, T const& b)
{
return (a > b) ? a : b;
}
Constraints can be used instead of SFINAE to removefunctions from the overload set.
template <typename T>
requires std:: is_integral <T>:: value
void ints_only(T i);
![Page 30: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/30.jpg)
Writing concepts
Defining good concepts is non-trivial. Concepts aremeant to represent fundamental concepts in anapplication domain (hence the name ”concepts”).Similarly throwing together a set of syntactic constraintsto be used for a the arguments for a single class oralgorithm is not what concepts were designed for and willnot give the full benefits of the mechanism.
From the C++ Core Guidelines 4
4https://github.com/isocpp/CppCoreGuidelines/blob/master/
CppCoreGuidelines.md#
t20-avoid-concepts-without-meaningful-semantics
![Page 31: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/31.jpg)
Writing concepts
In the current TS, concepts only allow checking syntacticalproperties.
However, concepts should only be designed from meaningfulsemantics (potentially including space/time complexity).
Unfortunately, we cannot have non-trivial semantics withoutproofs.
Concepts0x approximated proofs with axioms, in ConceptsLite they are gone completely.
![Page 32: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/32.jpg)
Writing concepts
Do not be mislead by the current feature state concepts!Concepts are not about syntax.
Always design your concepts on paper from clear, logicalsemantics.
Then try to identify the parts that overlap with what thecompiler can check.
Take the compiler’s help where you can get it. Document therest.
Important is not how much support you get from thecompiler, but how you think about your code.
![Page 33: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/33.jpg)
Thinking about concepts
Alex Stepanov’s books 5
Take a look at functional programming
5If you have only time for one, choose the one on the right
![Page 34: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/34.jpg)
Compiler support for concepts
Currently only on gcc 6.1 with -fconcepts
Stepanov-style concepts
#if !defined __cpp_concepts || \
__cpp_concepts < 201507
# define LTComparable typename
#endif
template <LTComparable T>
T const& min(T const& a, T const& b);
![Page 35: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/35.jpg)
Library Time!
![Page 36: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/36.jpg)
Vocabulary Types - std::optional
std::optional <Value > readValue ();
// ...
auto const v = readValue ();
if(v) { useValue (*v); }
useValue(v.value_or(default_value ));
![Page 37: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/37.jpg)
Constructing std::optional
auto empty = std::optional <Value >();
auto also_empty = std:: optional(std:: nullopt );
auto def_constr = std:: make_optional <Value >();
auto value = std:: make_optional(v);
auto from_args =
std:: make_optional <Value >(arg1 , arg2);
auto from_init_list =
std:: make_optional <std::vector <int >>
({1,2,3,4,5});
![Page 38: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/38.jpg)
Compiler support for std::optional
Supported in clang 4, gcc 7 and VC 15.
Also: Boost.Optional in Boost 1.30 with slightly differentinterface.
![Page 39: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/39.jpg)
Vocabulary Types - std::variant
std::variant <int , float , std::string > v = 42;
int n = std::get <int >(v);
v = std:: string("Narf");
std:: string s = std::get <std::string >(v);
float f = std::get <float >(v);
// throws std:: bad_variant_access
![Page 40: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/40.jpg)
Polymorphism with variants
struct Circle { float getArea () const; };
struct Square { float getArea () const; };
struct Triangle { float getArea () const; };
typedef variant <Circle , Square , Triangle > Shape;
struct GetShapeAreaVisitor {
template <typename T>
float operator ()(T const& s) {
return s.getArea ();
}
};
float getArea(Shape const& s) {
return std::visit(GetShapeAreaVisitor {}, s);
}
![Page 41: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/41.jpg)
Variant details
std::variant is almost-never-empty.
A variant always holds an instance of one of its Ts.
If a type-changing assignment throws, variant might have torevert to a special valueless statevalueless by exception().
In the absence of exceptions, variants give the never-emptyguarantee.
Supported in gcc 7 and VC 15.
Available with slightly different interface and semantics inBoost 1.31.
![Page 42: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/42.jpg)
Vocabulary Types - std::any
std::any any_value;
assert (! any_value.has_value ());
any_value = std::make_any <std::string >("narf");
any_value = std::make_any <int >(42);
int i = std::any_cast <int >( any_value );
auto* is_null =
std::any_cast <std::string >(& any_value );
std::any_cast <std::string >( any_value );
// throws std:: bad_any_cast
![Page 43: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/43.jpg)
Details of std::any
Prefer std::variant, if possible.
Type-erasure in std::any requires heap allocation.
Little better than a void*.
Supported in gcc 7, clang 4 and VC 15.
Available in Boost 1.23 with slightly different interface.
![Page 44: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/44.jpg)
Vocabulary Types - std::string view
void processString(char const* str);
// loses length
void processString(std:: string const& str);
// might make a copy
void processString(char const* str ,
std:: size_t len);
// yuck!
![Page 45: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/45.jpg)
Vocabulary Types - std::string view
void processString(std:: string_view const& str);
![Page 46: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/46.jpg)
Details of std::string view
A non-owning view on an immutable string.
Implemented as a std::basic string view template,similar to std::string.
Can be constructed from a std::string or a const pointer.
When constructing from pointer, if no length is given, stringmust be null-terminated and length will be computed atconstruction.
Constructing views on a substring requires no copy.
Supported in gcc 7, clang 4 and VC 15.
Available in the GSL(https://github.com/Microsoft/GSL)
![Page 47: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/47.jpg)
Ranges TS (not part of C++17)
The STL achieves great things by separating algorithms fromdata structures through iterators.
But iterators can be awkward to use.
c.erase(remove_if(begin(c), end(c), pred),
end(c));
Ranges consists of two parts:
An STL-like library that is based on ranges instead of iterators.Works with the existing STL containers.Concepts for both the classic STL and the new ranges parts
![Page 48: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/48.jpg)
Sentinels
Iterators point to elements.
A range in STL is spanned by an iterator to the first elementand an iterator to one-past-the-last.
Except that end iterator might not really point to anything. Ifwe are dealing with a range where not all elements are storedin memory, obtaining and end iterator might actually requireiterating the whole range.
Ranges introduces the notion of a sentinel.
A sentinel can be compared to an iterator. Both are equal ifthe iterator points to the end element.
A range in ranges is spanned by an iterator and a sentinel.
![Page 49: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/49.jpg)
What is a range?
Something that you can call begin and end on.
template <class T>
using iterator_t =
decltype(ranges ::begin(declval <T& >()));
template <class T>
using sentinel_t =
decltype(ranges ::end(declval <T& >()));
template <class T>
concept bool Range() {
return requires {
typename sentinel_t <T>;
};
};
![Page 50: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/50.jpg)
Ranges - Actions
In-place mutation.
Composable.
action :: remove_if(c, pred);
Pipe syntax:
vi = std::move(vi) |
action ::sort |
action :: unique;
![Page 51: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/51.jpg)
Ranges - Views
Non-mutating, lazy sequence manipulation.
Composable.
Cheap to create and copy.
auto odd = []( int i){ return i % 2 == 1; };
auto to_str = []( int i){
return std:: to_string(i);}
using namespace ranges;
std::vector <int > vi = view::ints (1 ,11);
// vi = {1,2,3,4,5,6,7,8,9,10};
auto rng = vi | view:: remove_if(odd)
| view:: transform(to_str );
// rng == {"2" ,"4" ,"6" ,"8" ,"10"};
![Page 52: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/52.jpg)
Ranges - Compiler support
You can use ranges today!
Reference implementation range-v36 works on gcc 4.8.5,clang 3.5.2; Microsoft offers a fork that works on VC 147
Full experience requires concepts language support by thecompiler, but even without that it’s a pretty great library.
Also: If you find bugs in the TS spec now, it is way easier tofix than once it goes IS.
6https://github.com/ericniebler/range-v37github.com/microsoft/Range-V3-VS2015
![Page 53: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/53.jpg)
Wrapping up...
Lots of features are available for use already today, especiallyfrom the library.
Small features can make a big difference in everyday work.
Other features might be bigger than they seem at first.
Don’t let lack of compiler support keep you from changinghow you think about your code today.
![Page 54: Cpp17 and Beyond](https://reader031.vdocuments.us/reader031/viewer/2022030213/589dbfe91a28abf7288b627b/html5/thumbnails/54.jpg)
Thanks for your attention.