C++ Annotated September 2021: Papers Voted Into C++23, enable_if vs. requires Benchmarks, More on ABI Breakage, and CppCon Program
We have all September news for you in our latest monthly C++ Annotated digest and its companion, the No Diagnostic Required show.
If you have already subscribed, feel free to skip straight to the news. If this is your first time, let us quickly take you through all the formats we offer. You can choose to read, listen, or watch our essential digest of the month’s C++ news:
- Read the monthly digest on our blog (use the form on the right to subscribe to the whole blog).
- Subscribe to C++ Annotated emails by filling out this form.
- Watch the No Diagnostic Required show on YouTube. To be notified of new episodes, follow us on Twitter.
- Listen to our podcast – just search for “No Diagnostic Required” in your favorite podcast player (see the list of supported players).
September news
- Language news
- Learning
- Tool updates
- And finally
Watch the September episode of No Diagnostic Required below, or keep reading for all the latest news!
Language News
October ISO Plenary
One of the advantages of writing the September edition at the start of October is that we are able to cover the October plenary meeting, which, at the time of writing, has just happened. The plenary is the meeting of the C++ standards committee (WG21) to vote on features of the next version of the standard (among other things).
9 language changes, 14 library changes (of which 4 were retrospectively applied as DRs (Defect Reports) to C++20), two combi papers containing issues from previous meetings, and the starting of a new TS for Transaction Memory! I won’t go into all of them in detail, but here are a few highlights, particularly as they relate to things we’ve discussed here before:
- P0798 – “Monadic operations for std::optional”. We’ve discussed this a few times – most recently back in May, when I said, “it seems the design is in good shape and we’re getting ready to poll it for inclusion into the working draft.” Making correct predictions is hard, especially about the future, but looks like I got that one right! It was voted into the C++23 draft today.
- P2128 – “Multidimensional subscript operator”. We covered this one back in April, at which point we commented it was quite a swift turnaround so soon after deprecating the comma operator in subscripting expressions. In another swift turnaround, it’s now coming in C++23.
- P2321: “zip”. Also discussed in April. Now zipped into the next standard.
- P0847 – “Deducing this”. This is an important paper, so here’s a brief summary.
In C++ today, every (non-static) member function has an implicit this
pointer. Not only is it implicit, but its form is dictated for you. It’s always a pointer, and whether it is a const (or a volatile) is determined by how you annotated the member function. That’s all usually fine, but, especially in generic library code, it can often lead to a lot of duplication. So it would be nice if we could make this an explicit argument – particularly a template argument. That way we could deduce its value category, for a start. That then opens up many further opportunities, such as passing it by value – very useful for cheap types like std::string_view
– which may pay a cost of indirection for passing by pointer. We can even deduce a derived type, if you’re calling from what is statically known as a derived instance. That gives us the possibility to do static polymorphism without having to use ugly and intrusive CRTP techniques. There are further possibilities, too, but hopefully this has whetted your appetite enough to go and read the paper.
Contracts
The “Minimum Contract Support […]” proposal gets another revision in P2388r2. This update mostly contains clarifications and expansions on what was already there, as well as a feature test macro. Those are all reassuringly boring, but there has been a lot more excitement going on outside the paper itself.
P2388 was meant to be the uncontroversial minimal subset of contract features. Yet some small concerns have been emerging. Much of these center around the syntax, which carries over the attribute-like notation that was in the ill-fated C++20 contracts. There is some concern that, even if they are not technically attributes, the double-square brackets are sufficiently evocative of attribute syntax, and that misconceptions may arise around, for example, what it means for compilation. There are also concerns that it may lead to incompatibilities with C’s adoption of this important feature.
Furthermore, while P2388 defers having to deal with the conundrum of postconditions that refer to mutable parameters (is it the value on exit, or a copy of the value on entry?) – by requiring that only const or reference parameters may appear in a postcondition – there is concern over whether locking in a syntax without addressing it now will restrict our options in the future.
As a response to all of this, one of the authors of P2388 started work on a new paper that explores just an alternate syntax. This new paper is not published yet, so I won’t go into the details. But I will say that, at least at this stage, the syntax takes some cues from lambdas – most notably by employing (optional) capture lists to specify how parameters are handled. Of course this all raises its own questions and these ideas are being actively discussed at the time of writing, ahead of a formal write-up, so the details may change. It remains to be seen whether the possibility of this new direction sets us back enough that contracts will miss the C++ 23 design cut-off. While many of us are keenly awaiting having even minimal contracts support in the language, we also want to see this done right. If we have to wait, then we will wait.
P2415R1 – What is a view?
This paper actually came out in July, then it was revised in August, but we haven’t discussed it before. It feels familiar, though – perhaps because there were long debates during the C++20 process about what constitutes a view and whether they are regular, semi-regular, or something else. For most developers these discussions may have seemed very abstract, and perhaps time and effort that could have been better spent elsewhere. But establishing a strong model is actually very important and has consequences long down the road. P2415 is more in the domain of dealing with those practical consequences – going back to what we were originally trying to model and adjusting our interpretation.
In C++20, views should be cheaply copyable or move-only – but in any case should be cheap to destroy (or rather, destruction should be O(1) – ruling out them containing copies of the data they are viewed over).
But that conflicts with a natural desire to be able to treat containers as views, themselves. If you have an rvalue container (e.g. returned, by value, from a function), then you get lifetime issues if the view can live longer than the container. That’s painful because, if the contained data is moved into a view, then there is no additional runtime overhead. So this paper proposes to relax the restriction on destruction, so views are allowed to be owning.
As always with these things, there are more subtleties to it – and this paper has not been without its critics. But these don’t seem to have been enough to prevent it from being voted into the working draft for C++23 during the plenary meeting and applied as a DR to C++20.
Views on views all the way down
While P2415 was from July and August, the September mailing has seven papers that relate to views – all but one of which are new papers! Five of these are from Barry Revzin and Tim Song, the authors of P2415, and they have almost contiguous paper numbers, so clearly submitted as a batch.
The two from Barry are P2446R0 (“views::move”) and P2441R0 (“views::join_with”). Tim’s are P2442R0 (“Windowing range adaptors: views::chunk and views::slide”) and P2443R0 (“views::chunk_by”). They also co-authored P2439R0, which is actually just slides that cover P2415R1 (“What is a view”). These guys certainly like to share their views!
The other two are David Olsen’s P2408R1 (“Ranges views as inputs to non-Ranges algorithms”) and Nico Josuttis’s P2432R0 (“Fix istream_view”). That last one was also just voted into the working draft for C++23 as well as being applied to C++20 as a DR.
Learning
A compile-time benchmark for enable_if
and requires
Arthur O’Dwyer ran some interesting benchmarking tests (part I, part II) to determine which constraints methodology is faster: SFINAE using std::enable_if
or C++20 constraints using the requires
keyword. Tl;dr: there is no clear winner across all compilers. The results differ from one compiler to another, and they also depend on the generator selected to produce translation units for the tests.
Several options for the SFINAE std::enable_if
in the test come from the libc++ approach that changed back in 2019, when standard enable_if_t
usages were updated to a slightly different technique with fewer instantiations and more type aliases being used. Another variation comes from either using the extra-value-parameter or extra-type-parameter approach in the generator.
All the tested options are described in the blog post. Interesting observations from the benchmarking results include:
- On Clang, the
requires
approach performs significantly worse in all tests. The return-type SFINAE approach gets the same low scores in the test that relies on template argument deduction instead of explicitly specializing the function. - On GCC, the
requires
approach shows average results. The extra-value-parameter approach always shows the best results. - On MSVC, the
requires
approach performs worse; the number of overloads in the test grows, but the share of the curve is different from the others. The return-type SFINAE approach wins out when explicitly specializing the function, but shows the worst results in the test that relies on template argument deduction.
C++ Return: std::any
, std::optional
, or std::variant
Rud Merriam wrote a guest blog post for the C++ Stories blog, sharing a practical example from robotics. The author explores solutions to a typical problem – how to handle requests that can bring data or no data at all, and how to avoid a test for no data after calling a decoding function each time. The article constructs and compares the following solutions: std::any
, std::optional
, and std::variant
.
std::any
can contain a value of any type without any specification of the type, and this looks quite flexible. But it turns out that you can’t avoid specifying a type, because you need it for the return data function.
std::variant
is analogous to a union. std::optional
is the closest to the problem specification (holds a value or no value). There is also an implementation that does not use inheritance (you might remember one of our previous episodes covered Arthur O’Dwyer’s article on why it’s unsafe to inherit from standard library types).
The article led to a long discussion thread with many proposals and improvements, which can be very useful if you are solving a similar task.
Coercing deep const-ness
Barry Revzin wrote a blog post about the const-qualification exception to the template deduction. The problem is that the function template that gets a pointer to a constant object of type T can actually be instantiated with a pointer to a mutable object. The blog post explores a few ways to force const-ness in this situation. The trick is that we don’t want to just require the incoming type to be constant – we want to force it to become constant.
The author tries using span
, C++20 concepts, and some code manually converting to const
, with various degrees of success. He then covers some possible solutions built into the language itself, which have been discussed in the community but don’t exist yet, such as deduction guides for function templates and C++0x (not C++20) concepts, which can alter interfaces. With the latter, you could indeed transform the result of calling the function on the underlying type and as a result, even if the argument were a mutable range, the parameter would still be a constant.
Binary Banshees and Digital Demons: a long read on ABI breaks
JeanHeyd Meneide wrote a treatise about ABI in C and C++, how it can be broken, and what consequences that entails. I encourage you to read it in full as a brief recap would not do it justice. This incredibly detailed article is, in my view, one of the best collections of ABI failures in C and C++, covering the following topics:
- How changing the copy constructor changes how it’s called.
- Why it’s a problem to have two identical C functions where one takes
long long
and the other takes__int128_t
. - Why you can never write 128-bit or 256-bit literals.
- The problems with nested functions.
- Why we’re stuck with a poorly performing
std::regex
. - Why the designers of Win32, POSIX, and Ethernet protocols libraries reserve bits just in case.
- The
std::format
locale issue. - The MSVC story behind
[[msvc::no_unique_address]]
(which you can also find below in this digest).
The author shares a lot of his frustrations with how deeply things are affected by the ABI contract. But putting feelings aside, the article provides invaluable insight into what is one of the biggest issues in modern language evolution.
Tool updates
MSVC C++20 and the /std:c++20
switch
In the beginning of September, Microsoft announced a sufficient level of C++20 support in MSVC (Visual Studio 2019 version 16.11) and added the /std:c++20
switch to the list of available switches. This means C++20 now has a stable switch and ABI compatibility is guaranteed (which is not the case for /std:c++latest
).
There is an interesting point regarding the ABI break in the announcement. The MSVC compiler ignores attributes that are not known, as allowed by the standard. This means the new C++20 [[no_unique_address]]
can cause issues. For example, compiling the same code under /std:c++17
and /std:c++20
switches can lead to different object layouts and thus to linking incompatibilities. In addition, linking with code compiled by an older compiler version might result in an ODR violation, for the same reasons.
To mitigate these issues, Microsoft decided to postpone the optimization for the attribute until the next ABI breakage in the MSVC. If you still want to force the optimization, consider using another new attribute, [[msvc::no_unique_address]]
. It works in versions VS 2019 v16.9 and higher, and for all compiler switches.
CLion 2021.3 EAP with enhancements for toolchains and the debugger
CLion’s third major release this year is now in early preview. The team has prioritized the debugging experience, especially embedded development, as well as making environments and toolchains easier to configure.
The CLion debugger has received an updated UI, with its main tool window and many panels rethought and streamlined. New tabs have been added like Parallel Stack View and FreeRTOS with tables of tasks, queues, timers, and a heap usage view. For embedded development, the debugger’s peripheral and hex views have been improved.
Environments and toolchains are now easier to set up thanks to the bundled MinGW and Ninja (which is now the default CMake generator in CLion), as well as the ability to configure an environment by sourcing a script.
Last but not least, CLion’s taking another step to make your code more readable, this time by bringing type hints for deduced types to the editor. The hints show the actual type in auto
variables and structured bindings, which should be useful for modern C++ where types can be omitted.
ReSharper C++ 2021.3 EAP helps modernize your C++ code
Both ReSharper and its sibling, ReSharper C++, are now available for Visual Studio 2022 Preview. Visual Studio finally became a 64-bit process, which also gives plugins more memory and improves their overall performance and responsiveness on large solutions.
As for product-specific improvements, ReSharper C++ helps encourage the adoption of recent changes in the language as well as best coding practices:
- The new standard library functions
contains
,erase
,erase_if
,make_shared_for_overwrite,
andmake_unique_for_overwrite
are actively promoted. - Modern best practices are suggested, such as removing a redundant elaborated type specifier, using a while-loop in some cases, applying the
[[nodiscard]]
attribute to a function without a return value, and more.
ReSharper C++ also helps you read and understand code more easily, thanks to an updated Quick Info tooltip which now includes the evaluation result for constant expressions.
SonarLint plugin for CLion
According to the results of our developer ecosystem survey, most developers rely on their IDE for static analysis. CLion’s capabilities in this area are vast, but they can be expanded even further with plugins such as SonarLint, released this summer. A new blog post from SonarSource explains how the plugin can benefit CLion users.
The post describes how some reports from the bundled Clang-Tidy can be improved to cover more specific cases, like detecting if parameters taken by pointer or reference could be made const not just for numeric types but for types like std::string
, as well.
The most intriguing part deals with security issues. What I personally found really interesting is how the compiler might optimize out the call to memset
to clear the passwords in memory when they are no longer used. Switching to calling memset_s
is suggested in this situation. And if you are using security and cryptography libraries in your code, there are checks in the plugin that detect many outdated or otherwise weak algorithms being used.
And finally: CppCon 2021 has announced its program
Scheduled for later in October, CppCon 2021 plans to be a hybrid event, and the main program is now live! Four tracks are planned for onsite attendees and five tracks for online attendees. Each talk will be marked with the corresponding access type, and some have both options available. The tracks include Back to Basics, Embedded, and a new Software Design track. And you can still join the conference – at least for online access.
We think the following talk announcements should especially be worthy of your attention:
- Bjarne Stroustrup will talk about whether we’re managing to reach the aims of C++ and achieve some ideals set. Specifically, he will discuss type-and-resource safe code, generic programming, modularity, and the elimination of the preprocessor.
- Titus Winters will teach you to design for the long term, discuss configurability versus concise design, and likely share Google’s experience in this area.
- Herb Sutter will demonstrate the C++ pattern-matching libraries and language proposals that the language committee is currently considering.
- Jason Turner will explain what constexpr is, give practical applications for constexpr, and help you figure out where constexpr fits into your application or library.
- Michael Caisse will focus on integrating C libraries and facilities within a modern C++ embedded target.
Phil and I will also each give a talk:
- Code Analysis++ by Anastasia Kazakova
- Zen and the Art of Code Lifecycle Maintenance by Phil Nash
Both of these sessions will be online and will focus on the various aspects and techniques of static code analysis and code quality in general.
See you at CppCon 2021!
About the authors
Anastasia Kazakova (@anastasiak2512) As a C/C++ software developer in Telecom and Embedded systems, Anastasia was involved in research estimating the broadband capacity of home networks and participated in launching 4G networks. She is now the Product Marketing Manager for JetBrains C++ tools. |
|
Phil Nash (@phil_nash) Phil is the original author of the C++ test framework Catch2. As Developer Advocate at SonarSource he’s involved with SonarQube, SonarLint, and SonarCloud, particularly in the context of C++. He’s also the organizer of C++ London and C++ on Sea, as well as co-host and producer of the cpp.chat podcast. More generally, Phil is an advocate for good testing practices, TDD, and using the type system and functional techniques to reduce complexity and increase correctness. He’s previously worked in Finance and Mobile and offers training and coaching in TDD for C++. |