From 0a3b3ffe665953e705c2d439ac22037900ac27be Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Mon, 27 Jul 2026 14:18:29 +0800 Subject: [PATCH 1/2] feat: vol4 metaprogramming sub-volume complete (04-09 CN+EN) - 04-09 CN: TMP techniques, compile-time strings, C++26 reflection, instantiation control, exception safety, mini-STL with concepts - EN translation for the whole sub-volume (01-09), voice-aligned with vol1 - 32 OnlineCompilerDemo (CN+EN) with allow-run, replacing bash run blocks - outputs aligned to actual runs (stdconcepts 11 lines, requires_expression) - subsumption demo: basic section links to full file on GitHub, full OnlineDemo moved after conjunction to avoid spoiling Both/C early - fix: add allow-run to vol2/05 push_back & noexcept_sort demos - cleanup: drop self-congratulatory sentences from vol4 three sub-volume indexes --- .../compile_time_hash.cpp | 31 +++ .../enum_to_string.cpp | 26 ++ .../extern_template/explicit_inst.cpp | 5 + .../extern_template/heavy_template.h | 39 +++ .../extern_template/main.cpp | 7 + .../extern_template/use_a.cpp | 8 + .../extern_template/use_b.cpp | 10 + .../fold_vs_recursion.cpp | 32 +++ .../mini_stl.cpp | 90 +++++++ .../move_if_noexcept_demo.cpp | 49 ++++ .../noexcept_propagation.cpp | 41 +++ .../nttp_cstr_pitfall.cpp | 30 +++ .../nttp_fixed_string.cpp | 49 ++++ .../sfinae_vs_concept.cpp | 39 +++ .../struct_members.cpp | 23 ++ .../structural_limit.cpp | 37 +++ .../tmp_factorial.cpp | 21 ++ .../traits_from_scratch.cpp | 29 ++ .../vector_realloc.cpp | 42 +++ .../void_t_detection.cpp | 25 ++ .../05-move-in-practice.md | 4 +- .../vol1-basics-cpp11-14/index.md | 2 - .../01-concepts.md | 223 +++++++++++++++ .../02-constraining-templates.md | 234 ++++++++++++++++ .../03-requires-expressions.md | 195 ++++++++++++++ .../04-tmp-core-techniques.md | 254 ++++++++++++++++++ .../05-compile-time-strings.md | 184 +++++++++++++ .../06-static-reflection-basics.md | 152 +++++++++++ .../07-template-instantiation-control.md | 151 +++++++++++ .../08-templates-and-exception-safety.md | 167 ++++++++++++ .../09-mini-stl-with-concepts.md | 151 +++++++++++ .../vol3-metaprogramming-cpp20-23/index.md | 40 ++- .../vol4-generics-patterns/index.md | 2 - .../05-move-in-practice.md | 4 +- .../vol1-basics-cpp11-14/index.md | 2 - .../01-concepts.md | 57 ++-- .../02-constraining-templates.md | 32 ++- .../03-requires-expressions.md | 26 +- .../04-tmp-core-techniques.md | 254 ++++++++++++++++++ .../05-compile-time-strings.md | 184 +++++++++++++ .../06-static-reflection-basics.md | 151 +++++++++++ .../07-template-instantiation-control.md | 151 +++++++++++ .../08-templates-and-exception-safety.md | 167 ++++++++++++ .../09-mini-stl-with-concepts.md | 151 +++++++++++ .../vol3-metaprogramming-cpp20-23/index.md | 10 +- .../vol4-generics-patterns/index.md | 2 - 46 files changed, 3519 insertions(+), 64 deletions(-) create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/compile_time_hash.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/enum_to_string.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/explicit_inst.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/heavy_template.h create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/main.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_a.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_b.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/fold_vs_recursion.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/mini_stl.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/move_if_noexcept_demo.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/noexcept_propagation.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_cstr_pitfall.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_fixed_string.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/sfinae_vs_concept.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/struct_members.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/structural_limit.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/tmp_factorial.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/traits_from_scratch.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/vector_realloc.cpp create mode 100644 code/examples/vol4/vol3-metaprogramming-cpp20-23/void_t_detection.cpp create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/01-concepts.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/02-constraining-templates.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/03-requires-expressions.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/04-tmp-core-techniques.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/05-compile-time-strings.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/06-static-reflection-basics.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/07-template-instantiation-control.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/08-templates-and-exception-safety.md create mode 100644 documents/en/vol4-advanced/vol3-metaprogramming-cpp20-23/09-mini-stl-with-concepts.md create mode 100644 documents/vol4-advanced/vol3-metaprogramming-cpp20-23/04-tmp-core-techniques.md create mode 100644 documents/vol4-advanced/vol3-metaprogramming-cpp20-23/05-compile-time-strings.md create mode 100644 documents/vol4-advanced/vol3-metaprogramming-cpp20-23/06-static-reflection-basics.md create mode 100644 documents/vol4-advanced/vol3-metaprogramming-cpp20-23/07-template-instantiation-control.md create mode 100644 documents/vol4-advanced/vol3-metaprogramming-cpp20-23/08-templates-and-exception-safety.md create mode 100644 documents/vol4-advanced/vol3-metaprogramming-cpp20-23/09-mini-stl-with-concepts.md diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/compile_time_hash.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/compile_time_hash.cpp new file mode 100644 index 000000000..9c83f4421 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/compile_time_hash.cpp @@ -0,0 +1,31 @@ +// 配套 05-compile-time-strings.md「编译期哈希:不一定非得 NTTP」 +// FNV-1a 编译期字符串哈希(纯 constexpr 函数,不需要 NTTP) +// 编译运行:g++ -std=c++20 -Wall -Wextra compile_time_hash.cpp -o cth && ./cth +#include +#include +#include + +// FNV-1a 编译期字符串哈希(纯 constexpr 函数,不需要 NTTP) +// 64 位魔法数是算法规定的 +constexpr std::uint64_t fnv1a_64(std::string_view s) { + std::uint64_t hash = 14695981039346656037ULL; // FNV offset basis + for (char c : s) { + hash ^= static_cast(static_cast(c)); + hash *= 1099511628211ULL; // FNV prime + } + return hash; +} + +int main() { + // 这些哈希值在编译期就算定 + constexpr auto h_hello = fnv1a_64("hello"); + constexpr auto h_hello2 = fnv1a_64("hello"); + constexpr auto h_world = fnv1a_64("world"); + + static_assert(h_hello == h_hello2, "same string, same hash"); + static_assert(h_hello != h_world, "different strings, different hash"); + + std::cout << "fnv1a_64(\"hello\") = " << h_hello << "\n"; + std::cout << "fnv1a_64(\"world\") = " << h_world << "\n"; + std::cout << "编译期哈希断言通过\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/enum_to_string.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/enum_to_string.cpp new file mode 100644 index 000000000..b8415b743 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/enum_to_string.cpp @@ -0,0 +1,26 @@ +// 配套 06-static-reflection-basics.md「实战二:枚举转字符串」 +// ⚠️ 本机 GCC16/clang22 不支持 P2996(C++26 反射)。 +// 在 Godbolt 选 clang_bb_p2996(Bloomberg 实验分支)编译运行,参数 -std=c++2c -freflection-latest +#include +#include +#include + +enum class Color { Red, Green, Blue }; + +// 反射版:枚举值 -> 名字,不用手写任何 switch/查表 +template constexpr std::string_view enum_to_string(E value) { + using namespace std::meta; + constexpr auto enumerators = define_static_array(enumerators_of(^^E)); + template for (constexpr auto enumerator : enumerators) { + if (value == [:enumerator:]) { + return identifier_of(enumerator); + } + } + return ""; +} + +int main() { + std::cout << enum_to_string(Color::Red) << "\n"; + std::cout << enum_to_string(Color::Green) << "\n"; + std::cout << enum_to_string(Color::Blue) << "\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/explicit_inst.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/explicit_inst.cpp new file mode 100644 index 000000000..629d03643 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/explicit_inst.cpp @@ -0,0 +1,5 @@ +#include "heavy_template.h" + +// 显式实例化定义:在这里实例化 Heavy 的全部成员, +// 供 use_b 等用了 extern template 声明的翻译单元链接 +template struct Heavy; diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/heavy_template.h b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/heavy_template.h new file mode 100644 index 000000000..93370a1d9 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/heavy_template.h @@ -0,0 +1,39 @@ +// 配套 07-template-instantiation-control.md「实测:机制怎么跑起来」 +// extern template + 显式实例化的多文件演示。本目录 5 个文件协作: +// heavy_template.h 模板定义(本文件) +// use_a.cpp 走老办法,隐式实例化 Heavy +// use_b.cpp 用 extern template 抑制实例化 +// explicit_inst.cpp 集中显式实例化 Heavy +// main.cpp 串起来 +// +// 编译运行: +// g++ -std=c++20 -Wall -Wextra -c use_a.cpp use_b.cpp explicit_inst.cpp main.cpp +// g++ use_a.o use_b.o explicit_inst.o main.o -o demo && ./demo +// +// 对照(去掉 explicit_inst.cpp,链接应失败,见正文「实测」节): +// g++ -std=c++20 -Wall -Wextra -c use_b.cpp main.cpp +// g++ use_b.o main.o -o demo_fail +#pragma once + +// 一个适度「重」的模板:实例化会生成若干成员代码 +template struct Heavy { + T value; + + explicit Heavy(T v) : value(v) {} + + T compute(T x) const { + T acc = value; + for (int i = 0; i < 10; ++i) { + acc = acc * x + value; + } + return acc; + } + + T scale(T factor) const { return value * factor; } + + T reset(T newv) { + T old = value; + value = newv; + return old; + } +}; diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/main.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/main.cpp new file mode 100644 index 000000000..d7eb44c5e --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/main.cpp @@ -0,0 +1,7 @@ +void use_a(); +void use_b(); + +int main() { + use_a(); + use_b(); +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_a.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_a.cpp new file mode 100644 index 000000000..88fb37894 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_a.cpp @@ -0,0 +1,8 @@ +#include "heavy_template.h" +#include + +// 这个翻译单元正常隐式实例化 Heavy(用到时编译器自动生成) +void use_a() { + Heavy h{42}; + std::cout << "use_a: " << h.compute(2) << "\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_b.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_b.cpp new file mode 100644 index 000000000..e8997bc0b --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/extern_template/use_b.cpp @@ -0,0 +1,10 @@ +#include "heavy_template.h" +#include + +// extern template 声明:Heavy 别处已经显式实例化,这里别再生成代码 +extern template struct Heavy; + +void use_b() { + Heavy h{99}; + std::cout << "use_b: " << h.compute(3) << "\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/fold_vs_recursion.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/fold_vs_recursion.cpp new file mode 100644 index 000000000..fc9e8ac98 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/fold_vs_recursion.cpp @@ -0,0 +1,32 @@ +// 配套 04-tmp-core-techniques.md「fold expressions:干掉递归样板」 +// 对照 variadic 递归老办法和 C++17 fold expression 的新写法 +// 编译运行:g++ -std=c++20 -Wall -Wextra fold_vs_recursion.cpp -o fvr && ./fvr +#include + +// 老办法:variadic 模板递归 + 终止函数,算任意数量参数之和 +template constexpr T sum_rec(T first) { + return first; +} + +template constexpr T sum_rec(T first, Rest... rest) { + return first + sum_rec(rest...); +} + +// C++17 fold expression:一行收掉上面那一坨 +template constexpr auto sum_fold(Ts... ts) { + return (ts + ...); // 一元右折叠 +} + +int main() { + std::cout << "sum_rec(1,2,3,4): " << sum_rec(1, 2, 3, 4) << "\n"; + std::cout << "sum_fold(1,2,3,4): " << sum_fold(1, 2, 3, 4) << "\n"; + + static_assert(sum_fold(1, 2, 3, 4) == 10); + static_assert(sum_fold(1, 2, 3, 4, 5, 6) == 21); + + // fold 还能就地展开,把一堆值「打印」出来(逗号折叠) + std::cout << "逗号折叠展开: "; + auto printer = [](auto x) { std::cout << x << " "; }; + (printer(1), printer(2.5), printer("hi")); + std::cout << "\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/mini_stl.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/mini_stl.cpp new file mode 100644 index 000000000..c639ce250 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/mini_stl.cpp @@ -0,0 +1,90 @@ +// 配套 09-mini-stl-with-concepts.md +// 用 C++20 concepts 约束的 mini-STL 算法库:transform / accumulate / find_if +// +// 编译运行: +// g++ -std=c++20 -Wall -Wextra mini_stl.cpp -o mstl && ./mstl +// 看传错类型的报错(NoPlus 不满足 Addable,concept 报错点名约束): +// g++ -std=c++20 -Wall -Wextra -DSHOW_ACC_ERROR mini_stl.cpp +#include +#include +#include +#include +#include +#include +#include + +namespace my { + +// 自定义 concept:支持 a + b 且结果能转成 U +template +concept Addable = requires(T a, U b) { + { a + b } -> std::convertible_to; +}; + +// 自定义 concept:能用 < 比较 +template +concept Ordered = requires(T a, T b) { + { a < b } -> std::convertible_to; +}; + +// transform:把 range 经 func 变换后写到 out +template + requires std::output_iterator> && + std::invocable> +Out transform(R&& r, Out out, F f) { + for (auto&& x : r) { + *out++ = std::invoke(f, x); + } + return out; +} + +// accumulate:累加 range 的元素到 init +template + requires Addable> +T accumulate(R&& r, T init) { + for (auto&& x : r) { + init = init + x; + } + return init; +} + +// find_if:找第一个满足 pred 的元素 +template + requires std::predicate> +std::ranges::borrowed_iterator_t find_if(R&& r, Pred pred) { + for (auto it = std::ranges::begin(r); it != std::ranges::end(r); ++it) { + if (std::invoke(pred, *it)) + return it; + } + return std::ranges::end(r); +} + +} // namespace my + +#ifdef SHOW_ACC_ERROR +struct NoPlus {}; // 没有 operator+ +int main() { + std::vector v(3); + my::accumulate(v, NoPlus{}); // Addable 约束不满足,concept 报错 +} +#else +int main() { + std::vector v = {1, 2, 3, 4}; + + std::vector squared; + my::transform(v, std::back_inserter(squared), [](int x) { return x * x; }); + std::cout << "transform 平方: "; + for (int x : squared) + std::cout << x << " "; + std::cout << "\n"; + + std::cout << "accumulate 求和: " << my::accumulate(v, 0) << "\n"; + + std::vector words = {"a", "b", "c"}; + std::cout << "accumulate 拼接: " << my::accumulate(words, std::string("start:")) << "\n"; + + auto it = my::find_if(v, [](int x) { return x % 2 == 0; }); + if (it != v.end()) + std::cout << "find_if 第一个偶数: " << *it << "\n"; +} +#endif diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/move_if_noexcept_demo.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/move_if_noexcept_demo.cpp new file mode 100644 index 000000000..735cfeeaf --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/move_if_noexcept_demo.cpp @@ -0,0 +1,49 @@ +// 配套 08-templates-and-exception-safety.md「move_if_noexcept:可能要回滚时,退回拷贝」 +// 演示 move_if_noexcept 怎么按 move 是否 noexcept 在 move/copy 之间挑 +// 编译运行:g++ -std=c++20 -Wall -Wextra move_if_noexcept_demo.cpp -o mind && ./mind +#include +#include +#include + +// 移动构造 noexcept:move_if_noexcept 会选 move +struct NothrowMove { + int* p; + explicit NothrowMove(int v) : p(new int(v)) {} + ~NothrowMove() { delete p; } + NothrowMove(const NothrowMove& o) : p(new int(*o.p)) { + std::cout << " [NothrowMove] 被拷贝\n"; + } + NothrowMove(NothrowMove&& o) noexcept : p(o.p) { + o.p = nullptr; + std::cout << " [NothrowMove] 被移动\n"; + } +}; + +// 移动构造可能抛:move_if_noexcept 会退回选 copy +struct ThrowingMove { + int* p; + explicit ThrowingMove(int v) : p(new int(v)) {} + ~ThrowingMove() { delete p; } + ThrowingMove(const ThrowingMove& o) : p(new int(*o.p)) { + std::cout << " [ThrowingMove] 被拷贝\n"; + } + ThrowingMove(ThrowingMove&& o) noexcept(false) : p(o.p) { // 故意 noexcept(false) + o.p = nullptr; + std::cout << " [ThrowingMove] 被移动\n"; + } +}; + +int main() { + std::cout << std::boolalpha; + std::cout << "is_nothrow_move_constructible:\n"; + std::cout << " NothrowMove: " << std::is_nothrow_move_constructible_v << "\n"; + std::cout << " ThrowingMove: " << std::is_nothrow_move_constructible_v << "\n"; + + std::cout << "move_if_noexcept 对 NothrowMove(应为 move):\n"; + NothrowMove nm(1); + [[maybe_unused]] auto nm2 = std::move_if_noexcept(nm); + + std::cout << "move_if_noexcept 对 ThrowingMove(应为 copy):\n"; + ThrowingMove tm(2); + [[maybe_unused]] auto tm2 = std::move_if_noexcept(tm); +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/noexcept_propagation.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/noexcept_propagation.cpp new file mode 100644 index 000000000..64951a7be --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/noexcept_propagation.cpp @@ -0,0 +1,41 @@ +// 配套 08-templates-and-exception-safety.md「条件 noexcept:把底层会不会抛如实传出去」 +// 对比「没标 noexcept」和「条件 noexcept」在调用方眼里的 noexcept 性 +// 编译运行:g++ -std=c++20 -Wall -Wextra noexcept_propagation.cpp -o nep && ./nep +#include +#include + +struct NoThrowMove { + NoThrowMove() = default; + NoThrowMove(NoThrowMove&&) noexcept {} // 显式 noexcept + NoThrowMove(const NoThrowMove&) = default; + NoThrowMove& operator=(NoThrowMove&&) noexcept { return *this; } +}; + +struct ThrowMove { + ThrowMove() = default; + ThrowMove(ThrowMove&&) {} // 没标,隐式可能抛 + ThrowMove(const ThrowMove&) = default; + ThrowMove& operator=(ThrowMove&&) { return *this; } +}; + +// 没标 noexcept:调用方只能保守地认为它可能抛 +template void uncond_op(T& x) { + T tmp(std::move(x)); + x = std::move(tmp); +} + +// 条件 noexcept:继承「T 的移动构造是否 noexcept」 +template void cond_op(T& x) noexcept(noexcept(T(std::move(x)))) { + T tmp(std::move(x)); + x = std::move(tmp); +} + +int main() { + std::cout << std::boolalpha; + std::cout << "uncond_op noexcept: " + << noexcept(uncond_op(std::declval())) << "\n"; + std::cout << "cond_op noexcept: " + << noexcept(cond_op(std::declval())) << "\n"; + std::cout << "cond_op noexcept: " + << noexcept(cond_op(std::declval())) << "\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_cstr_pitfall.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_cstr_pitfall.cpp new file mode 100644 index 000000000..fd559e0a6 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_cstr_pitfall.cpp @@ -0,0 +1,30 @@ +// 配套 05-compile-time-strings.md「先说麻烦:C++17 之前 const char* 作 NTTP 的难处」 +// 演示字符串字面量为什么不能直接作 NTTP(无 linkage) +// +// 默认编译跑:有 linkage 的 constexpr 变量能作 NTTP +// g++ -std=c++20 -Wall -Wextra nttp_cstr_pitfall.cpp -o cstr && ./cstr +// 看字面量直接作 NTTP 的报错: +// g++ -std=c++20 -Wall -Wextra -DSHOW_LITERAL_ERROR nttp_cstr_pitfall.cpp +#include + +// 有外部链接的 constexpr 字符串变量,能作 NTTP +constexpr const char kRed[] = "red"; + +template struct Tagged { + static constexpr const char* name = Name; +}; + +#ifdef SHOW_LITERAL_ERROR +template struct Bad {}; +Bad<"hello"> b; // 字符串字面量无 linkage,不能作 NTTP +#else +int main() { + std::cout << "kRed 的内容 = " << kRed << "\n"; + + // OK:kRed 是有 linkage 的对象,能作模板参数(数组到指针衰减) + Tagged t; + std::cout << "Tagged::name = " << t.name << "\n"; + std::cout + << "\n字符串字面量 \"hello\" 不能直接写进 template<...>,加 -DSHOW_LITERAL_ERROR 看报错\n"; +} +#endif diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_fixed_string.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_fixed_string.cpp new file mode 100644 index 000000000..68d275268 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/nttp_fixed_string.cpp @@ -0,0 +1,49 @@ +// 配套 05-compile-time-strings.md「C++20 的解药:P0732 与 structural type」 +// fixed_string 惯用法:把字符串包进 structural 结构体,当非类型模板参数(NTTP)用 +// 编译运行:g++ -std=c++20 -Wall -Wextra nttp_fixed_string.cpp -o nfs && ./nfs +#include + +// C++20:NTTP 可以是 class type,只要它是 structural(所有基类和数据成员 public) +// fixed_string:把字符串包进结构体,从而能当模板参数 +template struct FixedString { + char value[N] = {}; + + // 用 const char 数组构造(字面量 "abc" 是 const char[4],含 \0) + constexpr FixedString(const char (&str)[N]) { + for (std::size_t i = 0; i < N; ++i) { + value[i] = str[i]; + } + } + + constexpr bool operator==(const FixedString& other) const { + for (std::size_t i = 0; i < N; ++i) { + if (value[i] != other.value[i]) + return false; + } + return true; + } + + constexpr const char* c_str() const { return value; } +}; + +// CTAD 推导指引:让字面量 "hello" 推导出 FixedString<6>(含 \0) +template FixedString(const char (&)[N]) -> FixedString; + +// 把 FixedString 当 NTTP 用 +template struct Named { + static constexpr auto name = S; +}; + +template void greet() { + std::cout << "hello, " << S.c_str() << "\n"; +} + +int main() { + std::cout << Named<"world">{}.name.c_str() << "\n"; + greet<"templates">(); + + // 编译期字符串比较 + static_assert(FixedString{"abc"} == FixedString{"abc"}); + static_assert(!(FixedString{"abc"} == FixedString{"abd"})); + std::cout << "编译期字符串比较断言通过\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/sfinae_vs_concept.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/sfinae_vs_concept.cpp new file mode 100644 index 000000000..f1b897c71 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/sfinae_vs_concept.cpp @@ -0,0 +1,39 @@ +// 配套 04-tmp-core-techniques.md「SFINAE 往 concepts 迁移」 +// 同一需求(只接受整数)的两种写法:enable_if 老办法 vs concept 新办法 +// +// 默认编译跑:两种写法对 int 都正常 +// g++ -std=c++20 -Wall -Wextra sfinae_vs_concept.cpp -o svc && ./svc +// +// 看 SFINAE 版报错(用 string 触发,对照正文里 enable_if 的天书): +// g++ -std=c++20 -Wall -Wextra -DSHOW_SFINAE_ERROR sfinae_vs_concept.cpp +// +// 看 concept 版报错(对照正文里 constraints not satisfied 的人话): +// g++ -std=c++20 -Wall -Wextra -DSHOW_CONCEPT_ERROR sfinae_vs_concept.cpp +#include +#include +#include +#include + +// SFINAE 老办法:约束藏在默认模板参数里 +template >> T add_old(T a, T b) { + return a + b; +} + +// concept 新办法:约束写在签名里 +template + requires std::integral +T add_new(T a, T b) { + return a + b; +} + +int main() { +#ifdef SHOW_SFINAE_ERROR + add_old(std::string("a"), std::string("b")); +#elif defined(SHOW_CONCEPT_ERROR) + add_new(std::string("a"), std::string("b")); +#else + std::cout << "add_old(2, 3) = " << add_old(2, 3) << "\n"; + std::cout << "add_new(2, 3) = " << add_new(2, 3) << "\n"; + std::cout << "两种写法对 int 都正常;想看报错加 -DSHOW_SFINAE_ERROR 或 -DSHOW_CONCEPT_ERROR\n"; +#endif +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/struct_members.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/struct_members.cpp new file mode 100644 index 000000000..2ce93cf2e --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/struct_members.cpp @@ -0,0 +1,23 @@ +// 配套 06-static-reflection-basics.md「实战一:遍历 struct 的成员」 +// ⚠️ 本机 GCC16/clang22 不支持 P2996(C++26 反射)。 +// 在 Godbolt 选 clang_bb_p2996(Bloomberg 实验分支)编译运行,参数 -std=c++2c -freflection-latest +#include +#include + +struct Point { + int x; + int y; +}; + +int main() { + using namespace std::meta; + constexpr auto refl = ^^Point; + constexpr auto ctx = access_context::current(); + // define_static_array 把 vector 物化到静态存储,template for 才能编过 + constexpr auto members = define_static_array(nonstatic_data_members_of(refl, ctx)); + + std::cout << identifier_of(refl) << "\n"; + template for (constexpr auto member : members) { + std::cout << " " << identifier_of(member) << "\n"; + } +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/structural_limit.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/structural_limit.cpp new file mode 100644 index 000000000..a2b972ae1 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/structural_limit.cpp @@ -0,0 +1,37 @@ +// 配套 05-compile-time-strings.md「structural 的限制」 +// NTTP class type 要求类型是 structural:所有基类和非静态数据成员必须 public +// +// 默认编译跑:Open(public 数据成员)能作 NTTP +// g++ -std=c++20 -Wall -Wextra structural_limit.cpp -o sl && ./sl +// 看 Secret 不能作 NTTP 的报错:加 -DSHOW_NON_STRUCTURAL +// g++ -std=c++20 -Wall -Wextra -DSHOW_NON_STRUCTURAL structural_limit.cpp +#include + +// ✅ structural:public 数据成员 +struct Open { + int x; + int y; +}; + +template struct Wrapper { + static constexpr int sum = O.x + O.y; +}; + +#ifdef SHOW_NON_STRUCTURAL +// ❌ 非 structural:默认 class 的成员是 private +class Secret { + int x; // private + public: + constexpr Secret(int v) : x(v) {} +}; + +template struct Bad {}; // 报错:Secret 不是 structural type +Bad bad; +#else +int main() { + static_assert(Wrapper{}.sum == 7); + static_assert(Wrapper{}.sum == 30); + std::cout << "Open{3,4} 的 sum = " << Wrapper{}.sum << "\n"; + std::cout << "public 成员的 struct 能作 NTTP;private 成员的不行,加 -DSHOW_NON_STRUCTURAL 看\n"; +} +#endif diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/tmp_factorial.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/tmp_factorial.cpp new file mode 100644 index 000000000..7589f494a --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/tmp_factorial.cpp @@ -0,0 +1,21 @@ +// 配套 04-tmp-core-techniques.md「模板递归:用实例化做循环」 +// 演示经典 TMP 范式:模板递归 + 特化终止,在编译期算阶乘 +// 编译运行:g++ -std=c++20 -Wall -Wextra tmp_factorial.cpp -o tf && ./tf +#include + +// 经典 TMP:模板递归计算阶乘,靠特化提供终止条件 +template struct Factorial { + static constexpr unsigned value = N * Factorial::value; +}; + +template <> struct Factorial<0> { + static constexpr unsigned value = 1; +}; + +int main() { + std::cout << "Factorial<5>::value = " << Factorial<5>::value << "\n"; + std::cout << "Factorial<10>::value = " << Factorial<10>::value << "\n"; + static_assert(Factorial<5>::value == 120, "5! should be 120"); + static_assert(Factorial<10>::value == 3628800, "10! should be 3628800"); + std::cout << "编译期断言全部通过\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/traits_from_scratch.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/traits_from_scratch.cpp new file mode 100644 index 000000000..a082cc7d3 --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/traits_from_scratch.cpp @@ -0,0 +1,29 @@ +// 配套 04-tmp-core-techniques.md「type_traits 的内幕:特化就是编译期的 if-else」 +// 手写一个 is_pointer,看清 type_traits 「主模板 + 偏特化」的实现机制 +// 编译运行:g++ -std=c++20 -Wall -Wextra traits_from_scratch.cpp -o tfs && ./tfs +#include +#include + +// 手写一个 is_pointer:主模板兜底 false +template struct is_pointer_impl { + static constexpr bool value = false; +}; + +// 偏特化:匹配「指向 T 的指针」,true +template struct is_pointer_impl { + static constexpr bool value = true; +}; + +template constexpr bool is_pointer_v = is_pointer_impl::value; + +int main() { + std::cout << std::boolalpha; + std::cout << "is_pointer_v: " << is_pointer_v << "\n"; + std::cout << "is_pointer_v: " << is_pointer_v << "\n"; + std::cout << "is_pointer_v: " << is_pointer_v << "\n"; + std::cout << "is_pointer_v:" << is_pointer_v << "\n"; + // 和标准库对照,结果应一致 + static_assert(is_pointer_v == std::is_pointer_v); + static_assert(is_pointer_v == std::is_pointer_v); + std::cout << "与 std::is_pointer 结果一致\n"; +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/vector_realloc.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/vector_realloc.cpp new file mode 100644 index 000000000..6209b7e5f --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/vector_realloc.cpp @@ -0,0 +1,42 @@ +// 配套 08-templates-and-exception-safety.md「vector 扩容凭什么保住强异常保证」 +// vector 扩容时,move noexcept 的元素被 move,move 可能抛的元素被 copy(保强保证) +// 编译运行:g++ -std=c++20 -Wall -Wextra vector_realloc.cpp -o vr && ./vr +#include +#include + +// 故意写得能追踪 copy/move 次数 +struct NothrowMove { + int v; + explicit NothrowMove(int x) : v(x) {} + NothrowMove(const NothrowMove& o) : v(o.v) { std::cout << " copy\n"; } + NothrowMove(NothrowMove&& o) noexcept : v(o.v) { std::cout << " move\n"; } +}; + +struct ThrowingMove { + int v; + explicit ThrowingMove(int x) : v(x) {} + ThrowingMove(const ThrowingMove& o) : v(o.v) { std::cout << " copy\n"; } + ThrowingMove(ThrowingMove&& o) noexcept(false) : v(o.v) { std::cout << " move\n"; } +}; + +int main() { + std::cout << "vector 预留 2,再 push 第三个触发扩容:\n"; + { + std::vector v; + v.reserve(2); + v.emplace_back(1); + v.emplace_back(2); + std::cout << " >>> 扩容时(move noexcept,应为 move):\n"; + v.emplace_back(3); + } + + std::cout << "\nvector 预留 2,再 push 第三个触发扩容:\n"; + { + std::vector v; + v.reserve(2); + v.emplace_back(1); + v.emplace_back(2); + std::cout << " >>> 扩容时(move 可能抛,应为 copy 保强异常保证):\n"; + v.emplace_back(3); + } +} diff --git a/code/examples/vol4/vol3-metaprogramming-cpp20-23/void_t_detection.cpp b/code/examples/vol4/vol3-metaprogramming-cpp20-23/void_t_detection.cpp new file mode 100644 index 000000000..18b6e776e --- /dev/null +++ b/code/examples/vol4/vol3-metaprogramming-cpp20-23/void_t_detection.cpp @@ -0,0 +1,25 @@ +// 配套 04-tmp-core-techniques.md「void_t 与 detection idiom」 +// 用 void_t 的 detection idiom 检测类型是否有 value_type 内嵌类型 +// 编译运行:g++ -std=c++20 -Wall -Wextra void_t_detection.cpp -o vtd && ./vtd +#include +#include +#include +#include + +// detection idiom:用 void_t 检测 T 是否有 value_type 内嵌类型 +// 主模板:默认继承 false_type(给个 void 占位,匹配不上特化时落到这里) +template struct has_value_type : std::false_type {}; + +// 偏特化:只有当 void_t<...> 里的替换成功时,这个特化才「更特化」从而被选中 +template struct has_value_type> + : std::true_type {}; + +template constexpr bool has_value_type_v = has_value_type::value; + +int main() { + std::cout << std::boolalpha; + std::cout << "has_value_type_v>: " + << has_value_type_v> << "\n"; + std::cout << "has_value_type_v: " << has_value_type_v << "\n"; + std::cout << "has_value_type_v: " << has_value_type_v << "\n"; +} diff --git a/documents/en/vol2-modern-features/ch00-move-semantics/05-move-in-practice.md b/documents/en/vol2-modern-features/ch00-move-semantics/05-move-in-practice.md index 112dfa807..c1bccafb5 100644 --- a/documents/en/vol2-modern-features/ch00-move-semantics/05-move-in-practice.md +++ b/documents/en/vol2-modern-features/ch00-move-semantics/05-move-in-practice.md @@ -724,13 +724,13 @@ After copy construction, `arr2` owns an independent copy of the data; modifying Run the two examples and verify the key claims of this article yourself: - -