volatile and Other Small Improvements in C++20
Today, I complete my tour through the C++20 core language features with a few small improvements. One interesting of these minor improvements is that most of volatile has been deprecated.
volatile
The abstract in the proposal P1152R0 briefly describes the changes that volatile undergoes: “The proposed deprecation preserves the useful parts of volatile, and removes the dubious / already broken ones. This paper aims at breaking at compile-time code which is today subtly broken at runtime or through a compiler update. “
Before I show you what semantic of volatile is preserved, I want to start with the deprecated features:
- Deprecate volatile compound assignment, and pre/post increment/decrement
- Deprecate volatile qualification of function parameters or return types
- Deprecate volatile qualifiers in a structured binding declaration
If you want to know all the sophisticated details, I strongly suggest you watch the CppCon 2019 talk “Deprecating volatile” from JF Bastien. Here are a few examples from the talk referring to used numbers (1) to (3).
(1) int neck, tail; volatile int brachiosaur; brachiosaur = neck; // OK, a volatile store tail = brachiosaur; // OK, a volatile load // deprecated: does this access brachiosaur once or twice tail = brachiosaur = neck; // deprecated: does this access brachiosaur once or twice brachiosaur += neck; // OK, a volatile load, an addition, a volatile store brachiosau = brachiosaur + neck; ######################################### (2) // deprecated: a volatile return type has no meaning volatile struct amber jurassic(); // deprecated: volatile parameters aren't meaningful to the // caller, volatile only applies within the function void trex(volatile short left_arm, volatile short right_arm); // OK, the pointer isn't volatile, the data is opints to is void fly(volatile struct pterosaur* pterandon); ######################################## (3) struct linhenykus { volatile short forelimb; }; void park(linhenykus alvarezsauroid) { // deprecated: doe the binding copy the foreelimbs? auto [what_is_this] = alvarezsauroid; // ... }
I didn’t answer the crucial question: When should you use volatile? A note from the C++ standard says that “volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation.” This means for a single thread of execution that, the compiler must perform load or store operations in the executable as often as they occur in the source code. volatile operations, therefore, cannot be eliminated or reordered. Consequently, you can use volatile objects to communicate with a signal handler but not with another thread of execution.
To summarize, volatile avoids aggressive optimization and has no multithreading semantics.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
I present the remaining minor improvements with a short example that runs in the Compiler Explorer.
Range-based for-loop with Initializers
With C++20, you can directly use a range-based for-loop with an initializer.
// rangeBasedForLoopInitializer.cpp #include <iostream> #include <vector> int main() { for (auto vec = std::vector{1, 2, 3}; auto v : vec) { // (1) std::cout << v << " "; } std::cout << "\n\n"; for (auto initList = {1, 2, 3}; auto e : initList) { // (2) e *= e; std::cout << e << " "; } std::cout << "\n\n"; using namespace std::string_literals; for (auto str = "Hello World"s; auto c: str) { // (3) std::cout << c << " "; } }
The range-based for-loop uses in line (1) a std::vector, in line (2) a std::initializer_list, and line (3) a std::string. Additionally, in lines (1) and line (2), I apply automatic type deduction for class templates we have had since C++17. Instead of std::vector<int> and std::initalizer_list<int>, I write std::vector and std::initializer_list.
With GCC 10.2 and Compiler Explorer, I get the expected output.
virtual constexpr Functions
A constexpr function has the potential to run at compile-time but can also be executed at run-time. Consequently, you can make a constexpr function with C++20 virtual. Both directions are possible. Neither can a virtual constexpr function override a non-constexpr function nor can a virtual non-constexpr function override a constexpr virtual function. I want to emphasize that override implies that the regarding function of a base class is virtual.
The following program shows both combinations.
// virtualConstexpr.cpp #include <iostream> struct X1 { virtual int f() const = 0; }; struct X2: public X1 { constexpr int f() const override { return 2; } }; struct X3: public X2 { int f() const override { return 3; } }; struct X4: public X3 { constexpr int f() const override { return 4; } }; int main() { X1* x1 = new X4; std::cout << "x1->f(): " << x1->f() << std::endl; X4 x4; X1& x2 = x4; std::cout << "x2.f(): " << x2.f() << std::endl; }
Line (1) uses virtual dispatch (late binding) via a pointer, and line (2) uses virtual dispatch via reference. Once more, here is the output with GCC 10.2 and the Compiler Explorer.
The new Character Type for UTF-8 Strings: char8_t
Additionally, to the character types char16_t and char32_t from C++11, C++20 gets the new character type char8_t.char8_t is large enough to represent any UTF-8 code unit (8 bits). It has the same size, signedness, and alignment as an unsigned char, but is a distinct type.
Consequently, C++20 has a new typedef for the character type char8_t (1) and a new UTF-8 string literal (2).
std::u8string: std::basic_string<char8_t> (1) u8"Hello World" (2)
The following program shows the straightforward usage of char8_t:
// char8Str.cpp #include <iostream> #include <string> int main() { const char8_t* char8Str = u8"Hello world"; std::basic_string<char8_t> char8String = u8"helloWorld"; std::u8string char8String2 = u8"helloWorld"; char8String2 += u8"."; std::cout << "char8String.size(): " << char8String.size() << std::endl; std::cout << "char8String2.size(): " << char8String2.size() << std::endl; char8String2.replace(0, 5, u8"Hello "); std::cout << "char8String2.size(): " << char8String2.size() << std::endl; }
Without further ado. Here is the output of the program on the Compiler Explorer.
using enum in Local Scopes
A using enum declaration introduces the enumerators of the named enumeration in the local scope.
// enumUsing.cpp #include <iostream> #include <string_view> enum class Color { red, green, blue }; std::string_view toString(Color col) { switch (col) { using enum Color; // (1) case red: return "red"; // (2) case green: return "green"; // (2) case blue: return "blue"; // (2) } return "unknown"; } int main() { std::cout << std::endl; std::cout << "toString(Color::red): " << toString(Color::red) << std::endl; using enum Color; // (1) std::cout << "toString(green): " << toString(green) << std::endl; // (2) std::cout << std::endl; }
The using enum declaration in (1) introduces the enumerators of the scoped enumerations Color into the local scope. From then on, the enumerators can be used unscoped (2). This time, the only C++ compiler supporting using enum is the Microsoft Compiler 19.24:
Default member initializers for bit-fields
First, what is a bit field? Here is the definition of Wikipedia: “A bit field is a data structure used in computer programming. It consists of a number of adjacent computer memory locations which have been allocated to hold a sequence of bits, stored so that any single bit or group of bits within the set can be addressed.[1][2] A bit field is most commonly used to represent integral types of known, fixed bit-width.”
With C++20, we can default initialize the members of a bit-field:
// bitField.cpp #include <iostream> struct Class11 { // (1) int i = 1; int j = 2; int k = 3; int l = 4; int m = 5; int n = 6; }; struct BitField20 { // (2) int i : 3 = 1; int j : 4 = 2; int k : 5 = 3; int l : 6 = 4; int m : 7 = 5; int n : 7 = 6; }; int main () { std::cout << std::endl; std::cout << "sizeof(Class11): " << sizeof(Class11) << std::endl; std::cout << "sizeof(BitField20): " << sizeof(BitField20) << std::endl; std::cout << std::endl; }
According to the member of a class (1) with C++11, the members of the bit-field can have default initializers (2) with C++20. Finally, here is the output of the program with the Clang 10.0 compiler:
A Short Writing Break
In the next fortnight, I will be in Italy, so I will not write a regular post.
If you want to read one of my more than 300 posts on modern C++, I created a visual tour through my blog. This visual tour explains the TOC, categories, tags, archive, and search system and should help you find the post you are looking for.
Here you go https://youtu.be/hrXoVSi0O28.
What’s next?
After my short break, I continue my journey through C++20 with the new library. In particular, I will write about std::span.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,
Leave a Reply
Want to join the discussion?Feel free to contribute!