Notes on modern C++ programming [uts-002H]
Notes on modern C++ programming [uts-002H]
1. Scope
1. Scope
This note will cover some modern C++ features interesting to me during reading Modern C++ Programming.
Features standardized in C++23, or implemented for C++26, are in scope.
This note serves as a holistic view and a reminder.
2. Introduction
2. Introduction
Curiosity and persistence matter. -- Ben Cichy
C++ achieves uncompromised performance by living with undefined behavior, accepting a safety trade-off. -- This is C++: Uncompromised Performance, Undefined Behavior, & Move Semantics - Jon Kalb C++Now 2024
The problem with using C++...is that there’s already a strong tendency in the language to require you to know everything before you can do anything -- Larry Wall
3. Preparation
3. Preparation
- C++23 `std::print`
- Reducing C++ Compilation Times Through Good Design - Andrew Pearcy - ACCU 2024
4. Basic Concepts I
4. Basic Concepts I
- C++14 digit separators, e.g. `1'000'000` - C++14 `auto` for function return types, e.g. `auto f() { return 1; }` - C++20 `auto` for function input, equivalent to templates but less expensive at compile-time - C++20 `<utility>` for safely comparing signed/unsigned types
5. Basic Concepts II
5. Basic Concepts II
- Detecting wraparound for unsigned integral types is not trivial - C++23 `std::float128`, `std::binary16`, `std::bfloat16` - `inf` and `nan` for floating-point types - Floating-point Arithmetic Properties - Catastrophic Cancellation - Fixed epsilon “looks small” but it could be too large when the numbers being compared are very small - `areFloatNearlyEqual` - compensation algorithm like Kahan summation, Dekker’s FastTwoSum, Rump’s AccSum
6. Basic Concepts III
6. Basic Concepts III
- C++11/C++17/C++20 enum class, e.g. `using enum Color` - C++17 range-based loop for structure binding: `for (auto [key, value] : map)` - C++17/C++20 initializing statement in `if`/`switch` and range-for loop, e.g. `for (int i = 0; auto x : {'A', 'B', 'C'}) {}` - C++17 `[[maybe_unused]]` attribute, C++26 `auto _`
7. Basic Concepts IV
7. Basic Concepts IV
- C++11 Dynamic memory 2D allocation/deallocation: `new int[3][4]` and `delete[]` - `new (buffer)`: `delele[] buffer`, explicit `x->∼A()` - `new (std::nothrow)` - C++20 Designated Initializer List: `struct A { int x, y; }; A a{.x = 1, .y = 2};` - Pointer arithmetic rule: `address(ptr + i) = address(ptr) + (sizeof(T) * i)` - C++11 `constexpr`: _can_ be evaluated at compile-time - variable: always evaluated at compile-time - function - evaluated at compile-time as long as all its arguments are evaluated at compile-time - always evaluated at run-time if - contain run-time functions - contain references to run-time global variables - C++20 `consteval`: guarantees compile-time evaluation - A run-time value always produces a compile error - C++20 `constinit`: guarantees initialization at compile-time - A run-time initialization value always produces a compile error - The value of a variable _can_ change during the execution - C++17 `if constexpr`: compile-time branching - C++20 `std::is_constant_evaluated()` - C++23 `if consteval` - C++20 `std::bit_cast`
8. Basic Concepts V
8. Basic Concepts V
- `=delete` can be used to prevent calling the wrong overload - C++20 `[[nodiscard("reason")]]` - This issues a warning if the return value of a function is discarded (not handled), which is good for ensuring error handling. - C++17 for the entire class/struct - C++20 for constructors - C++23 for lambdas - The compiler injects `operator()` in the code of the destination function and then compile the routine. Operator inlining is the standard behavior - C++11 lambda expression: capture clause - `[]` captures nothing - `[&]` captures all variables by reference - `[=]` captures all variables by value - `[=, x, &y]` captures `x` by value and `y` by reference, all other variables by value - C++17 `[this]` captures the current object by reference - C++14 `[x=x]`, `[&x=x]` capture current object member `x` by value/reference - C++20 deprecates `[=]` capturing `this` pointer by value - C++14 `[](auto value)`, `[](int i = 6)` - A function can return a lambda (dynamic dispatch is also possible) - C++17 `constexpr` lambda, C++20 `consteval` lambda, `mutable` - syntax: post specifier - C++20 `template` and `requires` clause for lambda
auto lambda = []<typename T>(T value)
requires std::is_arithmetic_v<T> {
return value * 2;
};
auto v = lambda(3.4); // v: 6.8 (double)
struct A{} a;
// auto v = lambda(a); // compiler error
- When Preprocessors are Necessary - Conditional compiling - [Abseil Platform Check Macros](https://abseil.io/docs/cpp/platforms/macros) - Mixing different languages - Complex name replacing - [Are We Macro free Yet, CppCon2019](https://github.com/CppCon/CppCon2019/blob/master/Presentations/are_we_macrofree_yet/are_we_macrofree_yet__zhihao_yuan__cppcon_2019.pdf) - C++20 `<source location>` - replaces `__FILE__`, `__LINE__`, C++11 `__func__` - still: non-standard `__PRETTY_FUNCTION__` - C++17 [`__has_include`](https://en.cppreference.com/w/cpp/preprocessor/include) - stringizing macro operator `#`, e.g. `#string` expands to `"string"` - token pasting macro operator `##`, e.g. `x##y` expands to `xy` - `#error`, C++23 `#warning` - non-portable `pragma`, C++11 `_Pragma` - C++11 `__VA_ARGS__`
9. Object-Oriented Programming I
9. Object-Oriented Programming I
- C++11 In-class non-static data members initialization (NSDMI) - allows initializing the data members where they are declared - a user-defined constructor can be used to override their default values - C++11 uniform initialization `{}` - in function arguments and return values - C++11 delegate constructor - C++11 `=default`, `=delete` - `using` for type aliasing - e.g. partial specialization for templates
10. Object-Oriented Programming II
10. Object-Oriented Programming II
- C++11 `override` - ensures that the function is virtual and is overriding a virtual function from a base class - C++11 `final` - prevents a virtual function from being overridden in a derived class - `dynamic_cast` - upcast: `dynamic_cast<Base*>(derived)` - C++23 multidimensional subscript operator `[]` - C++23 static `[]` and `()` operators - C++11 `explicit` for conversion operators - `std:swap` - operators preserve precedence and short-circuit properties - binary operators should be implemented as friend methods - layouts - aggregate - initializable with `{}` - trivial copyable - can be copied with `memcpy` - standard layout - just like a struct or a union - POD - trivial copyable + standard layout - C++11/C++20 `<type_traits>` to check layout
11. Templates and Meta-programming I
11. Templates and Meta-programming I
- C++17 automatic deduction of non-type template parameters, with `auto` - non-type template parameters - C++20 a class literal type - `constexpr` assignable - public, non-mutable - for all base classes and non-static data members - pointer, reference, and pointer to member - function, e.g. `decltype(f)` - C++26 assertion with text formatting - e.g. `static_assert(sizeof(T) != 4, std::format("test1 with sizeof(T)={}", sizeof(T)));` - `decltype((expr))` for `lvalue` reference to the type of `expr` - C++14 `auto` for function template return types - replaces e.g. `decltype(T{} + R{})` - `<type_traits>` - type query - type manipulation
12. Templates and Meta-programming II
12. Templates and Meta-programming II
- C++17 CTAD - automatic deduction of class template arguments in constructor calls (CTAD) - template deduction guide - map constructor parameter types to class template parameters - e.g. `MyString(char const*) -> MyString<std::string>;` - doesn’t work within the class scope - C++20 alias template deduction - use `std::enable_if` to make use of SFINAE - helper alias: `_t` for `::type`, `_v` for `::value` - using `std::enable_if_t` for the return type prevents `auto` deduction - variadic template - captures a parameter pack of arguments, which hold an arbitrary number of values or types - ellipsis `...` - prefix an identifier to introduce/capture - suffix the identifier to expand - `sizeof...(args)` to get the number of arguments - C++17 fold `(other op ... op pack)` - C++14 for lambdas - C++20 for concepts - [C++20 idioms for parameter packs](https://www.scs.stanford.edu/~dm/blog/param-pack.html) - C++20 concepts - `concept [name] = [compile-time boolean expression];` - `requires` clause: `requires [compile-time boolean expression or Concept]` - `requires` expression:
#include <concept>
requires [(arguments)] {
[SFINAE contrain]; // or
requires [predicate];
} -> bool
template<typename T>
concept MyConcept = requires (T a, T b) { // First case: SFINAE constrains
a + b; // Req. 1 - support add operator
a[0]; // Req. 2 - support subscript operator
a.x; // Req. 3 - has "x" data member
a.f(); // Req. 4 - has "f" function member
typename T::type; // Req. 5 - has "type" field
{*a + 1} -> std::convertible_to<float>;// Req. 6 - can be deferred
// and the sum
// with an integer is convertible
// to float
{a * a} -> std::same_as<int>; // Req. 7 - "a * a" must be valid
// and the result type is "int"
};
13. Translation Units I
13. Translation Units I
- storage duration - static initialization order fiasco - One Definition Rule (ODR) - C++11 `extern template class A<int>;`
14. Translation Units II
14. Translation Units II
- common linker errors - multiple definitions - undefined reference - C++20 modules - a module is a set of source code files that are compiled independently of the translation units that import them - `module module.name;` - `import module.name;` - `export` - `export { ... }` - `export module module.name;` - `expor import module.name;` - global module fragment: include header files in a module interface - `module;` - private module fragment: include header files in a module implementation - `module :private;` - legacy headers can be directly imported with import - all declarations are implicitly exported and attached to the global module (fragment) - a module can be organized in isolated module partitions - namespace - anonymous namespace - visible only in the translation unit, internal linkage - inline namespace - can be used to version a library - C++17 attributes on namespaces - tools - `c++filt`, `ldd`, `nm`, `objdump`, `readelf`
15. Code Conventions
15. Code Conventions
- “Common” Project Organization - [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) - Naming style conventions can be also enforced by using tools like `clang-tidy`'s [readability-identifier-naming](https://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-naming.html#readability-identifier-naming) - Use braced direct-list-initialization or copy-initialization for setting default data member value. Avoid initialization in constructors if possible
struct A {
int x = 3; // copy-initialization
int x { 3 }; // direct-list-initialization (best option)
};
16. Debugging and Testing
16. Debugging and Testing
- Cost of Software Defects - 60 terrible tips for a C++ developer - Compiler Options Hardening Guide for C and C++ - Sanitizers - Static Analyzers - Linters
17. Ecosystem
17. Ecosystem
- xmake vs cmake - Compiler Explorer - CppInsights: see what your compiler does behind the scenes - grep.app: search git repos - Quick C++ Benchmark
18. Utilities
18. Utilities
- C++20 `std::format` - C++23 `std::print`, `std::println` - C++20 `std::span`: non-owning view of an underlying sequence or array - C++20 `<numeric>` - C++11 pseudo random number generation (PRNG) - C++11 `<chrono>` - overhead ~20-40ns - C++17 `<optional>`, `<any>` - C++23 `<stacktrace>`
19. Containers, Iterators, and Algorithms
19. Containers, Iterators, and Algorithms
- C++20 Ranges - operate on elements of data structures uniformly - pipe operator `|`, `|=` - `std::views::*`, `std::actions::*` - range factory: produces a view that contains no elements
20. Advanced Topics I
20. Advanced Topics I
- lvalue, rvalue (could be *p*ure rvalue, or e*x*piring *g*eneralized lvalue) - `std::move` indicates that an object is no longer needed and can be moved - C++11 reference collapsing rules - C++11 perfect forwarding with `std::forward`: allows preserving argument value category and const/volatile modifiers - copy elision for return value (C++17 guarantees some cases) - C++23 decay copy with `auto`
21. Advanced Topics II
21. Advanced Topics II
- C++23 `std::expected` - return a value or an `std::unexpected` - the user can choose how to handle the error: check return value, throw an exception, or monadic operations - future - `std::async(f, args)` - launch policy: `std::launch::async`, `std::launch::deferred` - `.get()` or `.wait()`
22. Performance Optimizations I
22. Performance Optimizations I
Mostly known conceptual introductions, good reference.
23. Performance Optimizations II
23. Performance Optimizations II
- C++11 `std:fma` - C++20 `<bit>` - C++20[[unlikely]]
- C++23[[assume(boolean expression)]]
24. Performance Optimizations III
24. Performance Optimizations III
- compiler flags and optimization - consider using optimized external libraries for critical program operations (good reference) - profiling tools - parallel computing - OpenACC - Intel OneAPI
25. Software Design I
25. Software Design I
- functions that are strongly suggested being non-member - binary operators - template functions within a class template - `std::linalg` proposal
26. Software Design II
26. Software Design II
- Curiously Recurring Template Pattern (CRTP): static polymorphism - Template Virtual Function