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