Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 <cstdint>
#include <iostream>
#include <string_view>

// 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<std::uint64_t>(static_cast<unsigned char>(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";
}
Original file line number Diff line number Diff line change
@@ -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 <iostream>
#include <meta>
#include <string_view>

enum class Color { Red, Green, Blue };

// 反射版:枚举值 -> 名字,不用手写任何 switch/查表
template <typename E> 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 "<unknown>";
}

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";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "heavy_template.h"

// 显式实例化定义:在这里实例化 Heavy<int> 的全部成员,
// 供 use_b 等用了 extern template 声明的翻译单元链接
template struct Heavy<int>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 配套 07-template-instantiation-control.md「实测:机制怎么跑起来」
// extern template + 显式实例化的多文件演示。本目录 5 个文件协作:
// heavy_template.h 模板定义(本文件)
// use_a.cpp 走老办法,隐式实例化 Heavy<int>
// use_b.cpp 用 extern template 抑制实例化
// explicit_inst.cpp 集中显式实例化 Heavy<int>
// 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 <typename T> 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;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
void use_a();
void use_b();

int main() {
use_a();
use_b();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "heavy_template.h"
#include <iostream>

// 这个翻译单元正常隐式实例化 Heavy<int>(用到时编译器自动生成)
void use_a() {
Heavy<int> h{42};
std::cout << "use_a: " << h.compute(2) << "\n";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "heavy_template.h"
#include <iostream>

// extern template 声明:Heavy<int> 别处已经显式实例化,这里别再生成代码
extern template struct Heavy<int>;

void use_b() {
Heavy<int> h{99};
std::cout << "use_b: " << h.compute(3) << "\n";
}
Original file line number Diff line number Diff line change
@@ -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 <iostream>

// 老办法:variadic 模板递归 + 终止函数,算任意数量参数之和
template <typename T> constexpr T sum_rec(T first) {
return first;
}

template <typename T, typename... Rest> constexpr T sum_rec(T first, Rest... rest) {
return first + sum_rec(rest...);
}

// C++17 fold expression:一行收掉上面那一坨
template <typename... Ts> 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";
}
90 changes: 90 additions & 0 deletions code/examples/vol4/vol3-metaprogramming-cpp20-23/mini_stl.cpp
Original file line number Diff line number Diff line change
@@ -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 <concepts>
#include <functional>
#include <iostream>
#include <iterator>
#include <ranges>
#include <string>
#include <vector>

namespace my {

// 自定义 concept:支持 a + b 且结果能转成 U
template <typename T, typename U = T>
concept Addable = requires(T a, U b) {
{ a + b } -> std::convertible_to<U>;
};

// 自定义 concept:能用 < 比较
template <typename T>
concept Ordered = requires(T a, T b) {
{ a < b } -> std::convertible_to<bool>;
};

// transform:把 range 经 func 变换后写到 out
template <std::ranges::input_range R, typename Out, typename F>
requires std::output_iterator<Out, std::ranges::range_value_t<R>> &&
std::invocable<F&, std::ranges::range_reference_t<R>>
Out transform(R&& r, Out out, F f) {
for (auto&& x : r) {
*out++ = std::invoke(f, x);
}
return out;
}

// accumulate:累加 range 的元素到 init
template <std::ranges::input_range R, typename T>
requires Addable<T, std::ranges::range_value_t<R>>
T accumulate(R&& r, T init) {
for (auto&& x : r) {
init = init + x;
}
return init;
}

// find_if:找第一个满足 pred 的元素
template <std::ranges::input_range R, typename Pred>
requires std::predicate<Pred&, std::ranges::range_reference_t<R>>
std::ranges::borrowed_iterator_t<R> 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<NoPlus> v(3);
my::accumulate(v, NoPlus{}); // Addable 约束不满足,concept 报错
}
#else
int main() {
std::vector<int> v = {1, 2, 3, 4};

std::vector<int> 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<std::string> 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
Original file line number Diff line number Diff line change
@@ -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 <iostream>
#include <type_traits>
#include <utility>

// 移动构造 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<NothrowMove> << "\n";
std::cout << " ThrowingMove: " << std::is_nothrow_move_constructible_v<ThrowingMove> << "\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);
}
Original file line number Diff line number Diff line change
@@ -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 <iostream>
#include <utility>

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 <typename T> void uncond_op(T& x) {
T tmp(std::move(x));
x = std::move(tmp);
}

// 条件 noexcept:继承「T 的移动构造是否 noexcept」
template <typename T> 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<NoThrowMove> noexcept: "
<< noexcept(uncond_op(std::declval<NoThrowMove&>())) << "\n";
std::cout << "cond_op<NoThrowMove> noexcept: "
<< noexcept(cond_op(std::declval<NoThrowMove&>())) << "\n";
std::cout << "cond_op<ThrowMove> noexcept: "
<< noexcept(cond_op(std::declval<ThrowMove&>())) << "\n";
}
Original file line number Diff line number Diff line change
@@ -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 <iostream>

// 有外部链接的 constexpr 字符串变量,能作 NTTP
constexpr const char kRed[] = "red";

template <const char* Name> struct Tagged {
static constexpr const char* name = Name;
};

#ifdef SHOW_LITERAL_ERROR
template <const char* Name> struct Bad {};
Bad<"hello"> b; // 字符串字面量无 linkage,不能作 NTTP
#else
int main() {
std::cout << "kRed 的内容 = " << kRed << "\n";

// OK:kRed 是有 linkage 的对象,能作模板参数(数组到指针衰减)
Tagged<kRed> t;
std::cout << "Tagged<kRed>::name = " << t.name << "\n";
std::cout
<< "\n字符串字面量 \"hello\" 不能直接写进 template<...>,加 -DSHOW_LITERAL_ERROR 看报错\n";
}
#endif
Loading
Loading