Open Source Your Knowledge, Become a Contributor
Technology knowledge has to be shared and made accessible for free. Join the movement.
Table Of Contents:
- Documents And Links
- Library Fundamentals V1 TS and more
std::any
std::variant
std::optional
std::string_view
- Searchers
- Other changes
- Summary
What I like about C++17 is that it finally brings a lot of features and patterns that are well known but come from other libraries. For example, for years programmers have been using boost libraries. Now, many of boost sub -libraries are merged into the standard. That merging process makes the transition to the modern C++ much easier, as most of the time the code will just compile and work as expected. Not to mention is the fact that soon you won't need any third party libraries.
Let's have a look at the following features:
std::any
- adapted from boost anystd::variant
- and the corresponding boost variantstd::optional
- boost optional librarystd::string_view
- Searchers for
std::search
- Plus a few other mentions
This playground is adapted from my blog: Bartek's coding blog: C++17 in details: Standard Library Utilities.
Visit the blog if you're looking for more good stuff about C++ :)
Documents & Links
First of all, if you want to dig into the standard on your own, you can read the latest draft here:
N4659, 2017-03-21, Draft, Standard for Programming Language C++ - from isocpp.org.
Also, you can grab my list of concise descriptions of all of the C++17 - It's a one-page reference card, pdf language features:
Grab it here: C++17 Reference Card
Links:
- Compiler support: C++ compiler support
- The official paper with changes: P0636r0: Changes between C++14 and C++17 DIS
- There's also a talk from Bryce Lelbach: C++Now 2017: C++17 Features
- My master C++17 features post: C++17 Features
- Jason Turner: C++ Weekly channel, where he covered most (or even all!) of C++17 features.
And the books:
OK, let's discuss the utils!
Library Fundamentals V1 TS and more
Most of the utilities described today (std::optional
,std::any
, std::string_view
, searchers) comes from so called "Library Fundamentals V1". It was in Technical Specification for some time, and with the paper "P0220R1 - Adopt Library Fundamentals V1 TS Components for C++17 (R1") it got merged into the standard.
Support:
- Libc++ C++1Z Status
- Visual Studio Support
- GCC/libstdc++, a lot of features are in
<experimental/>
namespace/headers.
When I describe the features, I write "compiler" support, but when discussing library features, I should mention the library implementation. For the sake of simplification, I'll just stick to compiler name as each common compiler (GCC, Clang, MSVC) have its separate libs.
And now the features:
std::any
A better way to handle any type and replace void*
.
Node from n4562:
The discriminated type may contain values of different types but does not attempt conversion between them, i.e. 5 is held strictly as an int and is not implicitly convertible either to "5" or to 5.0. This indifference to interpretation but awareness of type effectively allows safe, generic containers of single values, with no scope for surprises from ambiguous conversions.
In short, you can assign any value to existing any
object:
auto a = std::any(12);
a = std::string("hello world");
a = 10.0f;
When you want to read a value you have to perform a proper cast:
auto a = std::any(12);
std::cout << std::any_cast<int>(a) << '\n';
try
{
std::cout << std::any_cast<std::string>(a) << '\n';
}
catch(const std::bad_any_cast& e)
{
std::cout << e.what() << '\n';
}
Here's a bigger runnable sample (GCC 7.1):
Notes
any
object might be empty.any
shouldn't use any dynamically allocated memory, but it's not guaranteed by the spec.
More info in:
- n4562: any
- std::any - cppreference.com
- Boost.Any - 1.61.0
- c++ - What is the typical usage of boost any library? - Stack Overflow
- Conversations: I'd Hold Anything for You [1] | Dr Dobb's
MSVC VS 2017, GCC: 7.0, Clang: 4.0
std::variant
Type safe unions!
With a regular union
you can only use POD types (correction: since C++11 it's possible, assuming you provide required operation like a copy constructor, move... see union declaration)), and it's not safe - for instance, it won't tell you which variant is currently used. With std::variant
it's only possible to access types that are declared.
For example:
std::variant<int, float, std::string> abc;
abc
can only be initialized with int
, float
or string
and nothing else. You'll get a compile time error when you try to assign something else.
To access the data, you can use:
std::get
with index or type of the alternative. It throwsstd::bad_variant_access
on errors.std::get_if
- returns a pointer to the element ornullptr
;- or use
std::visit
method that has usage especially for containers with variants.
A bigger playground (GCC 7.1):
Notes:
- Variant is not allowed to allocate additional (dynamic) memory.
- A variant is not permitted to hold references, arrays, or the type void.
- The first alternative must always be default constructible
- A variant is default initialized with the value of its first alternative.
- If the first alternative type is not default constructible, then the variant must use
std::monostate
as the first alternative
More info:
- P0088R3: Variant: a type-safe union for C++17 (v8). - note that Variant wasn't in the Library Fundamentals, it was a separate proposal.
MSVC VS 2017, GCC: 7.0, Clang: 4.0?
std::optional
Another and elegant way to return objects from functions that are allowed to be empty.
For example:
std::optional<std::string> ostr = GetUserResponse();
if (ostr)
ProcessResponse(*ostr);
else
Report("please enter a valid value");
In the simple sample above GetUserResponse
returns optional with a possible string inside. If a user doesn't enter a valid value ostr
will be empty. It's much nicer and expressive than using exceptions, nulls, output params or other ways of handling empty values.
A better example (GCC 7.1):
Notes:
- Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value. The contained value shall be allocated in a region of the optional storage suitably aligned for the type T.
More info:
- n4562: optional
- Boost Optional
- Efficient optional values | Andrzej's C++ blog
- Recipe "Safely signalizing failure with std::optional" from C++17 STL Cookbook.
MSVC VS 2017, GCC: 7.0, Clang: 4.0?
string_view
Although passing strings got much faster with move semantics from C++11, there's still a lot of possibilities to end up with many temporary copies.
A much better pattern to solve the problem is to use a string view. As the name suggests instead of using the original string, you'll only get a non-owning view of it. Most of the time it will be a pointer to the internal buffer and the length. You can pass it around and use most of the common string functions to manipulate.
Views work well with string operations like sub string. In a typical case, each substring operation creates another, smaller copy of some part of the string. With string view, substr
will only map a different portion of the original buffer, without additional memory usage, or dynamic allocation.
Another important reason for using views is the consistency: what if you use other implementations for strings? Not all devs have the luxury to work only with the standard strings. With views, you can just write (or use) existing conversion code, and then string view should handle other strings in the same way.
In theory string_view
is a natural replacement for most of const std::string&
.
Still, it's important to remember that it's only a non-owning view, so if the original object is gone, the view becomes rubbish.
If you need a real string, there's a separate constructor for std::string
that accepts a string_view
. For instance, the filesystem library was adapted to handle string view (as input when creating a path object).
Ok, but let's play with the code (GCC 7.1):
More info:
- n4562:
string_view
and also N3921, string_view: a non-owning reference to a string, revision 7 - What is string_view? - Stack Overflow
- C++17 string_view – Steve Lorimer
- Modernescpp - string_view
- foonathan::blog() - std::string_view accepting temporaries: good idea or horrible pitfall?
MSVC VS 2017, GCC: 7.0, Clang: 4.0?
Searchers
When you want to find one object in a string
, you can just use find or some other alternative. But the task complicates when there's a need to search for a pattern (or a sub range) in a string.
The naive approach might be O(n*m)
(where n
is the length of the whole string, m
is the length of the pattern).
But there are much better alternatives. For example Boyer-Moore with the complexity of O(n+m)
.
C++17 updated std::search
algorithm in two ways:
- you can now use execution policy to run the default version of the algorithm but in a parallel way.
- you can provide a Searcher object that handles the search.
For now we have three searchers:
default_searcher
boyer_moore_searcher
boyer_moore_horspool_searcher
You can play with the example here (GCC 7.1):
- Which version is the fastest?
- Is this better than just
std::string::find
?
More info:
MSVC VS 2017.3, GCC: 7.0, Clang: 3.9?
Other Changes
shared_ptr
with array - P0414R2: Merging shared_ptr changes from Library Fundamentals to C++17. So farunique_ptr
was able to handle arrays. Now it's also possible to useshared_ptr
.- Splicing Maps and Sets - PDF P0083R2 - we can now move nodes from one tree based container (maps/sets) into other ones, without additional memory overhead/allocation.
- Mathematical special functions - PDF: P0226R1
- Improving
std::pair
andstd::tuple
- N4387) - pair/tuple obey the same initialization rules as their underlying element types. - Sampling - n4562: Sampling - new algorithm that selects
n
elements from the sequence - Elementary string conversions - P0067R5, new function
to_chars
that handles basic conversions, no need to use stringstream, sscanf, itoa or other stuff.
Summary
Did I miss something? Yes!
There are many other changes in STL that would fill another post (or I could expand the "Other Changes" section). But let's stop for now. Note that each of those 'small' utils are worth a separate post, with more example, so I'll definitely plan to do that later :)
If you want to dig deeper try to read the spec/draft or look at the official paper with changes: P0636r0: Changes between C++14 and C++17 DIS.
As I mentioned, I like that C++17 merged many useful well-known patterns into STL. There's a high chance you've come across many of the features and using them in a project shouldn't be that hard.
What do I like the most?
I think:
- Filesystem - a significant portion of the library, that will make code much easier and common across many platforms.
- type safe helpers:
std::any
,std::optional
,std::variant
- we can now replacevoid*
or C style unions. The code should be safer. - string features: like
string_view
, string conversions, searchers. - parallelism - very powerful abstraction for threading.
Still, there's a lot of stuff to learn/teach! I've just described the features, but the another part of the equation is to use them effectively. And that needs experience.
- What are your favourite features from C++17 STL?
- What have I missed? What else should be in my C++17 posts?
- Have you already used any/optional/variant, for example from boost?