This month’s C++ topic overview! (2025-08)
1. Structured Binding Variables Do Not Trigger NRVO
#include <iostream>#include <tuple>#include <utility>
struct T { T() = default; T(const T&) { std::cout << "Copy constructor called\n"; } T(T&&) noexcept { std::cout << "Move constructor called\n"; }};
std::pair<int, T> foo() { return {std::piecewise_construct, std::forward_as_tuple(42), std::forward_as_tuple()};}
T func() { auto [x, y] = foo(); // copy!!! return y;}
int main() { T t = func(); return 0;}
Output: “Copy constructor called”.
Structured binding is actually still a single variable, giving each field an alias, rather than directly constructing multiple variables.
NRVO (Named Return Value Optimization) constructs the local variable directly at the return value’s address. For sub-objects, you cannot replace this sub-object with the function return value; other sub-objects use the original local variable. Therefore, RVO/NRVO is impossible.
So it should be written as <span>return std::move(y);</span>.
2. Dispatching bool Variables to bool Template Arguments
A simple approach is to use a macro to pass bool to a constexpr variable:
#include <iostream>
#define DISPATCH_BOOL(condition, name, ...) \ if (condition) { \ constexpr bool name = true; \ (__VA_ARGS__)(); \ } else { \ constexpr bool name = false; \ (__VA_ARGS__)(); \ }
template <bool Flag>void foo() { printf("%d\n", Flag);}
int main() { for (bool flag : {false, true}) { DISPATCH_BOOL(flag, Flag, [] { foo<Flag>(); }); } return 0;}
Using templates instead of macros is also possible:
#include <iostream>#include <type_traits>
template <typename lambda_t>auto dispatch_bool(bool condition, const lambda_t& lambda) { if (condition) { return lambda.template operator()<true>(); } else { return lambda.template operator()<false>(); }}
template <bool Flag>void foo() { printf("%d\n", Flag);}
int main() { for (bool flag : {false, true}) { dispatch_bool(flag, []<bool Flag> { foo<Flag>(); }); }}
A group member also provided a method using <span>std::visit</span>, which supports multiple bools, but it seems a bit verbose:
#include <iostream>#include <type_traits>#include <variant>
std::variant<std::false_type, std::true_type> inline make_bool_variant( bool condition) { if (condition) { return std::true_type{}; } else { return std::false_type{}; }}
int main() { for (auto [a, b, c] : {std::tuple{0, 0, 1}, std::tuple{1, 0, 1}}) { std::visit( [&](auto a, auto b, auto c) { printf("%d %d %d\n", decltype(a)::value, decltype(b)::value, decltype(c)::value); }, make_bool_variant(a), make_bool_variant(b), make_bool_variant(c)); }}
3. Temporary Objects are Pure Rvalues
It is well known that <span>std::vector{1} = std::vector{2};</span> can compile successfully.
Another example is:
struct T { T& operator=(T) { return *this; }};
struct V { V& operator=(V) && { return *this; }};
int main() { T{} = T{}; // ok V{} = V{}; // error: Candidate function not viable: expects an lvalue for object argument}
The copy function of V restricts to lvalues, hence it fails to compile.
4. ASan Used for Poisoning Code
class AsanPoisonDefer {#ifdef ADDRESS_SANITIZERpublic: // Poison the memory region to prevent accidental access // during the lifetime of this object. AsanPoisonDefer(const void* start, size_t len) : start(start), len(len) { ASAN_POISON_MEMORY_REGION(start, len); } // Unpoison the memory region when this object goes out of scope. ~AsanPoisonDefer() { ASAN_UNPOISON_MEMORY_REGION(start, len); }
private: const void* start; size_t len;#elsepublic: // No-op for platforms without ASAN_DEFINE_REGION_MACROS AsanPoisonDefer(const void*, size_t) {} ~AsanPoisonDefer() = default;#endif};
Manual poisoning prevents memory from being out of bounds, but logically it is out of bounds.
For example, when accessing <span>vec[0]</span>, poison <span>vec[1]</span>.
Mainly used in memory pools, where out-of-bounds does not report errors, only manual poisoning.
5. Mathematical Functions (e.g., std::sqrt) are Not constexpr
Because mathematical functions can be affected by rounding modes and may set errno, which means they have global state read/write. Starting from C++26, they will become constexpr, using if consteval to actively determine whether to return different results based on compile-time evaluation.
Reference article: https://www.zhihu.com/question/265774676/answer/3471569930
6. How to Bind Different Types of Variables Under Different Compile-Time Conditions
Similar to <span>auto val = cond ? int{} : std::string{};</span>
This can be done as follows:
auto val = [] { if constexpr (cond) { return int{}; } else { return std::string{}; }}();
Note that if the else is removed, it will report an error, as shown in the code below. This is because the function return type deduction requires that statements within constexpr if may not participate in deduction.
auto val = [] { if constexpr (cond) { return int{}; } return std::string{}; // return type 'std::string' (aka 'basic_string<char>') must match previous return type 'int' when lambda expression has unspecified explicit return type}();
7. <span>buildin_ctz(0)</span> is Undefined Behavior
Yes. ref https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html#index-_005f_005fbuiltin_005fctz
This is because processors generally output 32 states (5 bits), and processing 0 requires 33 states, which is not supported.
The builtin likely aims to simplify things, so it defines 0 as undefined behavior. If it were to be universal, it would handle 0 specially, for example, <span>std::countr_zero</span> is universal, and on x86, the assembly is as follows:
#include <bit>
int foo1(unsigned x) { return std::countr_zero(x); // xor eax, eax // mov edx, 32 // rep bsf eax, edi // test edi, edi // cmove eax, edx // ret}
int foo2(unsigned x) { [[assume(x != 0)]]; return std::countr_zero(x); // xor eax, eax // rep bsf eax, edi // ret}
8. Why C++ Vector’s push_back Expansion Mechanism Does Not Consider Allocating Memory Behind the Last Element
Reference article: https://www.zhihu.com/question/384869006
The key point is that the realloc interface is not flexible; it automatically allocates memory and memcpy when in-place expansion fails, without calling the move constructor. This necessitates that objects be trivially copyable.
C++ does not yet have a similar function in the standard; see C++’s relocate semantics – YKIKO’s article – Zhihu https://zhuanlan.zhihu.com/p/679782886
9. An Example of Template Template Parameters: Allocator Rebind
This operation is quite remarkable.
If you want to write a template container (like a red-black tree), the user provides a node’s allocator type <span>Allocator<Node></span>, and the template container can take the allocator and rebind it to another type <span>Allocator<Other></span>.
10. Parent Class 16 Bytes, Child Class with Member Variables Still 16 Bytes
This is because the parent class has padding for byte alignment, and in some cases, the child class can fit member variables into this padding.
For example:
#include <cstdint>#include <cstdio>
class A { int64_t a; int32_t b;};
class B : A { int32_t c;};
int main() { printf("%zu %zu\n", sizeof(A), sizeof(B)); // Output 16 16 (GCC 15.1)}
Issue 7 of C++ Monthly Highlights has a more complex example: the impact of public/private inheritance on memory layout.
11. constexpr std::source_location::current Loses Template Information
#include <iostream>#include <string_view>#include <source_location>
template <class T>constexpr std::string_view foo1() { constexpr auto tmp = std::source_location::current(); return tmp.function_name();}
template <class T>constexpr std::string_view foo2() { auto tmp = std::source_location::current(); return tmp.function_name();}
int main() { std::cout << foo1<int>() << '\n'; // constexpr std::string_view foo1() std::cout << foo2<int>() << '\n'; // constexpr std::string_view foo2() [with T = int; std::string_view = std::basic_string_view<char>]}
In foo2, <span>std::source_location</span> tries to reflect the calling point’s information as much as possible, so it delays the evaluation stage to the template instantiation stage to allow <span>function_name</span> to include template information.
In foo1, because <span>std::source_location</span> does not depend on template parameters, constexpr evaluates it at compile time, thus losing the template information.
12. Analyzing Computer System Performance with Perl PDQ (Book)
Shared by a group member:
Having finished the first chapter, this book has little to do with Perl. The first chapter analyzes how to evaluate the relationship between system load, throughput, and latency.
The second chapter introduces several very old performance sampling tools, which can be skipped. The later sections discuss how to evaluate whether the load rate’s sampling results meet the Poisson distribution.
The third chapter discusses concepts of time, how to understand time, system timestamps, how to measure time accurately, and how to fit discrete response times into various distributions, including special scenarios like distributed servers, the relationship between availability and downtime, failure rates and reliability, and reliability models.
The fourth chapter covers basic concepts of queuing theory, mathematical definitions, Little’s law, and derives formulas for single-server systems, calculating average residence time of tasks using utilization and average service time, and average queue length using utilization. This leads to understanding several empirical rules regarding utilization. The formulas are then extended to multi-server systems, where high-load situations yield some counterintuitive results through mathematical reasoning. Following this, Erlang’s C and B formulas are provided for more precise analysis of busy probabilities and task drop probabilities in high-load multi-server systems. Using these formulas, one can estimate how to accurately scale the system to achieve a certain availability metric. The chapter concludes with a discussion of several servers, essentially polling algorithms and task flows. It then derives the PK equation, guiding how to calculate the average residence time of each queue based on historical metrics to assist in selecting waiting queues for new tasks, noting that this auxiliary decision-making algorithm is inferior to polling. Finally, the author, known as the father of the universal scalability law, introduces the universal scalability law.
The fifth chapter begins to consider combinations of multiple computer subsystems, such as hard drives + memory + CPU, to analyze multi-queue situations, while the fourth chapter considered multiple interfaces of the same performance.
13. Some Articles
Group leader teaches resume writing live replay https://www.bilibili.com/video/BV1BNtzz8E7A/ (although not an article)
C++ Chinese Weekly 2025-08-11 Issue 188 https://wanghenshui.github.io/cppweeklynews/posts/188.html
High-frequency related, article on Mo’s trading competition https://zhuanlan.zhihu.com/p/470766162
Understanding and Implementing SIMD Functions https://johnnysswlab.com/the-messy-reality-of-simd-vector-functions/
What open-source memory pools in C++ are worth learning? (Article) https://www.zhihu.com/question/663670460/answer/1942914503420417900