第三部分:类型系统与泛型
[!IMPORTANT] Java 开发者必读:C++ 模板与 Java 泛型的根本差异。
Java 泛型 C++ 模板 实现机制 类型擦除(编译后变成 Object)编译时实例化(为每个类型生成独立代码) 运行时信息 泛型类型信息被擦除 每个实例化是独立的完整类型 约束方式 <T extends Number>Concepts (C++20) / SFINAE / static_assert性能开销 有装箱/拆箱开销 零开销抽象(编译后与手写代码等价) 可见性 可以 new T()不能直接实例化模板参数(除非有约束) 代码膨胀 无(一份字节码) 有(每种类型一份代码) 一句话总结:Java 泛型是"语法糖 + 运行时检查",C++ 模板是"编译期代码生成器"。C++ 模板更强大,但也更复杂,编译错误更难读。
3.1 模板基础 [L3]
3.1.1 泛型机制对比
| 特性 | Java/Kotlin | C++ |
|---|---|---|
| 声明语法 | <T> | template<typename T> |
| 函数泛型 | <T> T max(T a, T b) | template<typename T> T max(T a, T b) |
| 类泛型 | class Box<T> | template<typename T> class Box |
| 类型约束 | <T extends Number> | requires std::integral<T> (C++20) |
| 多类型参数 | <K, V> | template<typename K, typename V> |
| 默认类型参数 | 不支持 | template<typename T = int> |
| 运行时类型 | 擦除为 Object | 保留完整类型信息 |
| 方法重载 | 泛型不参与重载 | 模板可以重载,也可以特化 |
| 递归泛型 | class Node<T> | template<typename T> struct Node |
| 原始类型 | 有(Box = Box<?>) | 无(必须指定类型参数) |
3.1.2 函数模板
#include <iostream>
#include <string>
// 基础函数模板
template<typename T>
T max_value(T a, T b) {
return (a > b) ? a : b;
}
// 多类型参数
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
// C++14:返回类型推导
template<typename T, typename U>
auto multiply(T a, U b) {
return a * b;
}
int main() {
std::cout << "=== 函数模板 ===\n";
std::cout << "max(3, 7) = " << max_value(3, 7) << "\n";
std::cout << "max(3.14, 2.72) = " << max_value(3.14, 2.72) << "\n";
std::cout << "max(\"abc\", \"xyz\") = " << max_value<std::string>("abc", "xyz") << "\n";
std::cout << "\nadd(3, 4.5) = " << add(3, 4.5) << "\n";
std::cout << "multiply(2, 3.5) = " << multiply(2, 3.5) << "\n";
return 0;
}
编译命令:
g++ -std=c++23 -Wall -Wextra -O2 func_template.cpp -o func_template && ./func_template
3.1.3 类模板
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
// 泛型栈 —— 对比 Java 的 Stack<E>
template<typename T, std::size_t Capacity = 16>
class Stack {
public:
Stack() : data_(Capacity), size_(0) {}
void push(const T& value) {
if (size_ >= Capacity) throw std::overflow_error("Stack overflow");
data_[size_++] = value;
}
void push(T&& value) {
if (size_ >= Capacity) throw std::overflow_error("Stack overflow");
data_[size_++] = std::move(value);
}
T pop() {
if (size_ == 0) throw std::underflow_error("Stack underflow");
return std::move(data_[--size_]);
}
T& top() {
if (size_ == 0) throw std::underflow_error("Stack underflow");
return data_[size_ - 1];
}
const T& top() const {
if (size_ == 0) throw std::underflow_error("Stack underflow");
return data_[size_ - 1];
}
std::size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
private:
std::vector<T> data_;
std::size_t size_;
};
int main() {
std::cout << "=== 类模板:泛型栈 ===\n";
// 默认容量 16
Stack<int> int_stack;
int_stack.push(10);
int_stack.push(20);
int_stack.push(30);
std::cout << "int_stack top: " << int_stack.top() << "\n";
std::cout << "int_stack pop: " << int_stack.pop() << "\n";
std::cout << "int_stack size: " << int_stack.size() << "\n";
// 指定容量
Stack<std::string, 4> str_stack;
str_stack.push("hello");
str_stack.push("world");
std::cout << "str_stack top: " << str_stack.top() << "\n";
return 0;
}
3.1.4 CTAD — 类模板参数推导 (C++17)
CTAD 让你省略模板参数,编译器从构造函数参数推导。类似 Java 的钻石操作符 <>,但更强大——Java 的 <> 只是省略已知的类型参数,CTAD 是真正从参数推导。
#include <iostream>
#include <utility>
#include <complex>
#include <vector>
#include <string>
// 自定义 CTAD 推导指引
template<typename T>
class Pair {
public:
Pair(T first, T second) : first_(first), second_(second) {}
T first() const { return first_; }
T second() const { return second_; }
private:
T first_, second_;
};
// 推导指引:允许不同类型构造
template<typename T, typename U>
Pair(T, U) -> Pair<std::common_type_t<T, U>>;
int main() {
std::cout << "=== CTAD (C++17) ===\n";
// C++17 之前必须写:std::pair<int, double> p(1, 2.5);
std::pair p1{1, 2.5}; // CTAD 推导为 pair<int, double>
std::cout << "pair: (" << p1.first << ", " << p1.second << ")\n";
// vector 的 CTAD
std::vector v1{1, 2, 3, 4, 5}; // 推导为 vector<int>
std::vector v2{"hello"s, "world"s}; // 推导为 vector<string>(注意 s 后缀)
std::cout << "vector<int>: ";
for (auto x : v1) std::cout << x << " ";
std::cout << "\n";
std::cout << "vector<string>: ";
for (auto& x : v2) std::cout << x << " ";
std::cout << "\n";
// 自定义 CTAD
Pair p3{1, 2}; // 推导为 Pair<int>
Pair p4{1.5, 2.5}; // 推导为 Pair<double>
Pair p5{1, 2.5}; // 推导指引 → Pair<double>
std::cout << "Pair<int>: (" << p3.first() << ", " << p3.second() << ")\n";
std::cout << "Pair<double>: (" << p4.first() << ", " << p4.second() << ")\n";
std::cout << "Pair<common_type>: (" << p5.first() << ", " << p5.second() << ")\n";
return 0;
}
CTAD 详解:隐式推导 vs 显式推导
CTAD 分为隐式推导(编译器自动)和显式推导(通过推导指引)。
| 推导方式 | 语法 | 控制权 | Java 对应 |
|---|---|---|---|
| 隐式推导 | std::pair p{1, 2.5}; | 编译器根据构造函数推导 | Map<String, Integer> map = new HashMap<>(); |
| 显式推导指引 | template<...> Pair(...) -> Pair<...>; | 程序员自定义推导规则 | 无(Java 无推导指引概念) |
Java 对比:Java 的钻石操作符
<>只是省略已知的类型参数(编译器从变量声明中推断),不是真正的推导。C++ 的 CTAD 是从构造函数参数真正推导模板参数,甚至可以自定义推导规则。
// 代码片段(无 main 函数)
#include <vector>
#include <string>
#include <utility>
// 隐式 CTAD —— 编译器自动推导
void implicit_ctad() {
std::pair p1{1, 2.5}; // pair<int, double>
std::vector v1{1, 2, 3}; // vector<int>
std::vector v2{"a"s, "b"s}; // vector<string>(注意 s 后缀)
std::mutex m1; // mutex(无参构造)
std::lock_guard lk(m1); // lock_guard<std::mutex>
}
// 显式推导指引 —— 自定义推导规则
template<typename T>
class Box {
T value_;
public:
explicit Box(T v) : value_(std::move(v)) {}
T get() const { return value_; }
};
// 推导指引 1:从单个参数推导
template<typename T>
Box(T) -> Box<T>;
// 推导指引 2:从多个参数推导为公共类型
template<typename T, typename U>
Box(T, U) -> Box<std::common_type_t<T, U>>;
void explicit_ctad() {
Box b1{42}; // Box<int>(推导指引 1)
Box b2{3.14}; // Box<double>
Box b3{1, 2.5}; // Box<double>(推导指引 2,公共类型)
// 没有推导指引时:Box b1{42}; 会编译错误,因为 Box 的构造函数不暴露模板参数
}
3.1.5 变参模板 (Variadic Templates, C++11)
变参模板是 C++ 模板最强大的特性之一。Java 没有等价物——它允许模板接受任意数量、任意类型的参数。
#include <iostream>
#include <string>
// 基础递归终止
void print() {
std::cout << "\n";
}
// 递归展开:每次处理一个参数
template<typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first;
if constexpr (sizeof...(rest) > 0) {
std::cout << ", ";
}
print(rest...);
}
// C++17 折叠表达式 —— 更简洁的变参展开
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // 一元右折叠:(a1 + (a2 + (a3 + ...)))
}
// C++17 折叠表达式:逗号折叠打印
template<typename... Args>
void print_fold(Args... args) {
((std::cout << args << " "), ...); // 逗号折叠
std::cout << "\n";
}
// 变参模板类:类型安全的 printf
// 对比 Java 的 String.format("%s %d", ...) —— C++ 版本在编译期检查类型
void safe_printf(const char* fmt) {
while (*fmt) std::cout << *fmt++;
}
template<typename T, typename... Args>
void safe_printf(const char* fmt, T value, Args... args) {
while (*fmt) {
if (*fmt == '%' && *(fmt + 1) != '%') {
std::cout << value;
safe_printf(fmt + 1, args...);
return;
}
std::cout << *fmt++;
}
}
// 计算包中参数数量
template<typename... Args>
constexpr std::size_t count_args() {
return sizeof...(Args);
}
int main() {
std::cout << "=== 变参模板 ===\n";
// 递归展开
std::cout << "递归 print: ";
print(1, "hello", 3.14, "world", 42);
// 折叠表达式
std::cout << "sum(1,2,3,4,5) = " << sum(1, 2, 3, 4, 5) << "\n";
std::cout << "sum(1.5, 2.5, 3.5) = " << sum(1.5, 2.5, 3.5) << "\n";
// 逗号折叠打印
std::cout << "折叠 print: ";
print_fold("alpha", 42, 3.14, "beta");
// 类型安全 printf
std::cout << "safe_printf: ";
safe_printf("Name: %s, Age: %s, Score: %s\n",
std::string("Alice"), 30, 95.5);
// 参数计数
std::cout << "count_args<int, double, char>: "
<< count_args<int, double, char>() << "\n";
return 0;
}
Fold Expressions 详解 (C++17)
Fold Expressions 是 C++17 引入的语法,用于展开参数包(parameter pack),替代手动的递归展开。
语法形式:
| 形式 | 展开方式 | 含义 |
|---|---|---|
(pack op ...) | 一元右折叠 | (e1 op (e2 op (e3 op ...))) |
(... op pack) | 一元左折叠 | (((... op e1) op e2) op e3) |
(pack op ... op init) | 二元右折叠 | (e1 op (e2 op (... op (en op init)))) |
(init op ... op pack) | 二元左折叠 | ((((init op e1) op e2) op ...) op en) |
其中 op 可以是:+ - * / % ^ & \| << >> && \|\| , .= 等 32 个运算符。
Java 对比:Java 的可变参数 (
T... args) 只能通过循环遍历处理,没有折叠表达式。C++ 的 Fold Expressions 在编译期展开,零运行时开销。
// demo_fold_expressions.cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// === 基础折叠 ===
// 一元右折叠:求和
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // (a1 + (a2 + (a3 + ...)))
}
// 一元左折叠:求和(结果相同,但折叠方向不同,对非交换运算符有区别)
template<typename... Args>
auto sum_left(Args... args) {
return (... + args); // (((a1 + a2) + a3) + ...)
}
// 带初始值的二元折叠:处理空参数包
template<typename... Args>
auto sum_safe(Args... args) {
return (0 + ... + args); // 空包时返回 0,不会编译错误
}
// 逗号折叠:对每个参数执行操作
template<typename... Args>
void print_all(Args... args) {
((std::cout << args << " "), ...); // 对每个参数打印
std::cout << "\n";
}
// 折叠表达式:全部满足条件(类似 Java Stream.allMatch)
template<typename... Args>
bool all_positive(Args... args) {
return (... && (args > 0));
}
// 折叠表达式:任一满足条件(类似 Java Stream.anyMatch)
template<typename... Args>
bool any_negative(Args... args) {
return (... || (args < 0));
}
// 折叠表达式:压入 vector
template<typename... Args>
std::vector<std::common_type_t<Args...>> to_vector(Args... args) {
std::vector<std::common_type_t<Args...>> result;
(result.push_back(args), ...); // 逗号折叠
return result;
}
// 折叠表达式:调用成员函数
template<typename... Args>
void sort_all(Args&... containers) {
(std::sort(containers.begin(), containers.end()), ...);
}
int main() {
std::cout << "=== Fold Expressions 详解 ===\n\n";
// 基础折叠
std::cout << "sum(1,2,3,4,5) = " << sum(1, 2, 3, 4, 5) << "\n";
std::cout << "sum_left(1,2,3,4,5) = " << sum_left(1, 2, 3, 4, 5) << "\n";
std::cout << "sum_safe() = " << sum_safe() << " (空包,返回初始值)\n";
// 逗号折叠
std::cout << "\nprint_all: ";
print_all(1, "hello", 3.14, "world");
// 逻辑折叠
std::cout << "all_positive(1,2,3) = " << all_positive(1, 2, 3) << "\n";
std::cout << "all_positive(1,-2,3) = " << all_positive(1, -2, 3) << "\n";
std::cout << "any_negative(1,2,3) = " << any_negative(1, 2, 3) << "\n";
std::cout << "any_negative(1,-2,3) = " << any_negative(1, -2, 3) << "\n";
// 转换为 vector
auto vec = to_vector(1, 2, 3, 4, 5);
std::cout << "\nto_vector: [";
for (size_t i = 0; i < vec.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << vec[i];
}
std::cout << "]\n";
// 对多个容器排序
std::cout << "\n=== 对多个容器排序 ===\n";
std::vector<int> v1{5, 3, 1, 4, 2};
std::vector<int> v2{10, 30, 20};
sort_all(v1, v2); // 一次调用排序多个容器
std::cout << "v1: ";
for (int x : v1) std::cout << x << " ";
std::cout << "\nv2: ";
for (int x : v2) std::cout << x << " ";
std::cout << "\n";
return 0;
}
编译运行:
g++ -std=c++17 -Wall -Wextra -O2 demo_fold_expressions.cpp -o demo_fold && ./demo_fold
3.1.6 非类型模板参数
Java 不支持非类型模板参数。这是 C++ 独有的特性——允许用值(而非类型)作为模板参数。
#include <iostream>
#include <array>
// 非类型模板参数:编译期常量
template<typename T, int Size>
class FixedArray {
public:
T& operator[](int index) { return data_[index]; }
const T& operator[](int index) const { return data_[index]; }
constexpr int size() const { return Size; }
private:
T data_[Size]; // Size 是编译期常量,可以直接用作数组大小
};
// 非类型模板参数:用作策略
template<int Threshold>
class ThresholdFilter {
public:
bool operator()(int value) const {
return value > Threshold;
}
static constexpr int threshold() { return Threshold; }
};
// C++20:非类型模板参数可以用浮点数和类类型
template<double Factor>
constexpr double scale(double value) {
return value * Factor;
}
// auto 非类型模板参数 (C++17)
template<auto Value>
struct Constant {
static constexpr auto value = Value;
};
int main() {
std::cout << "=== 非类型模板参数 ===\n";
// 编译期大小
FixedArray<int, 5> arr;
for (int i = 0; i < arr.size(); ++i) {
arr[i] = i * 10;
}
std::cout << "FixedArray<int, 5>: ";
for (int i = 0; i < arr.size(); ++i) {
std::cout << arr[i] << " ";
}
std::cout << "\n";
// 策略模式(编译期)
ThresholdFilter<10> filter10;
ThresholdFilter<100> filter100;
std::cout << "filter<10>(15): " << filter10(15) << "\n";
std::cout << "filter<10>(5): " << filter10(5) << "\n";
std::cout << "filter<100>(50): " << filter100(50) << "\n";
std::cout << "filter<100>(200): " << filter100(200) << "\n";
// 浮点非类型参数
std::cout << "scale<2.5>(4.0) = " << scale<2.5>(4.0) << "\n";
// auto 非类型参数
std::cout << "Constant<42>::value = " << Constant<42>::value << "\n";
std::cout << "Constant<'X'>::value = " << Constant<'X'>::value << "\n";
return 0;
}
3.1.7 综合示例:泛型容器 + CTAD + 变参模板
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
// ==================== 泛型容器 ====================
template<typename T>
class RingBuffer {
public:
explicit RingBuffer(std::size_t capacity)
: data_(capacity), head_(0), tail_(0), count_(0), capacity_(capacity) {}
void push(const T& value) {
if (count_ == capacity_) throw std::overflow_error("RingBuffer full");
data_[tail_] = value;
tail_ = (tail_ + 1) % capacity_;
++count_;
}
T pop() {
if (count_ == 0) throw std::underflow_error("RingBuffer empty");
T value = std::move(data_[head_]);
head_ = (head_ + 1) % capacity_;
--count_;
return value;
}
std::size_t size() const { return count_; }
bool empty() const { return count_ == 0; }
bool full() const { return count_ == capacity_; }
private:
std::vector<T> data_;
std::size_t head_, tail_, count_, capacity_;
};
// ==================== CTAD 推导指引 ====================
template<typename T>
RingBuffer(std::vector<T>) -> RingBuffer<T>;
// ==================== 变参模板工具 ====================
// 类型安全的格式化函数
template<typename... Args>
std::string format(std::string_view fmt, const Args&... args) {
std::ostringstream oss;
std::size_t arg_index = 0;
auto tuple_args = std::tie(args...);
auto print_arg = [&](auto&& arg) {
if constexpr (std::is_floating_point_v<std::decay_t<decltype(arg)>>) {
oss << std::fixed << std::setprecision(2) << arg;
} else {
oss << arg;
}
};
for (std::size_t i = 0; i < fmt.size(); ++i) {
if (fmt[i] == '{' && i + 1 < fmt.size() && fmt[i + 1] == '}') {
std::apply([&](const auto&... a) {
int idx = 0;
((idx++ == static_cast<int>(arg_index) ? print_arg(a) : void()), ...);
}, tuple_args);
++arg_index;
++i; // skip '}'
} else {
oss << fmt[i];
}
}
return oss.str();
}
// 变参 to_string
void collect(std::vector<std::string>&) {}
template<typename T, typename... Args>
void collect(std::vector<std::string>& out, const T& first, const Args&... rest) {
std::ostringstream oss;
if constexpr (std::is_same_v<T, double>) {
oss << std::fixed << std::setprecision(2) << first;
} else {
oss << first;
}
out.push_back(oss.str());
collect(out, rest...);
}
template<typename... Args>
std::vector<std::string> to_strings(const Args&... args) {
std::vector<std::string> result;
collect(result, args...);
return result;
}
int main() {
std::cout << "========== 综合示例 ==========\n\n";
// 1. 泛型环形缓冲区
std::cout << "--- RingBuffer<int> ---\n";
RingBuffer<int> buffer(3);
buffer.push(10);
buffer.push(20);
buffer.push(30);
std::cout << "size: " << buffer.size() << ", full: " << buffer.full() << "\n";
std::cout << "pop: " << buffer.pop() << "\n";
buffer.push(40); // 覆盖已弹出的位置
while (!buffer.empty()) {
std::cout << "pop: " << buffer.pop() << "\n";
}
std::cout << "\n--- RingBuffer<std::string> ---\n";
RingBuffer<std::string> str_buf(4);
str_buf.push("hello");
str_buf.push("world");
std::cout << "pop: " << str_buf.pop() << "\n";
// 2. CTAD
std::cout << "\n--- CTAD ---\n";
std::pair p{1, "hello"}; // pair<int, const char*>
std::cout << "pair: (" << p.first << ", " << p.second << ")\n";
std::tuple t{42, 3.14, "test"}; // tuple<int, double, const char*>
std::cout << "tuple: (" << std::get<0>(t) << ", "
<< std::get<1>(t) << ", " << std::get<2>(t) << ")\n";
// 3. 变参模板
std::cout << "\n--- 变参模板 ---\n";
auto strs = to_strings(42, 3.14, "hello", 100);
std::cout << "to_strings: [";
for (std::size_t i = 0; i < strs.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << strs[i];
}
std::cout << "]\n";
std::cout << "format: " << format("Name={}, Age={}, Score={}", "Alice", 30, 95.678) << "\n";
return 0;
}
3.2 模板进阶 [L4]
3.2.1 SFINAE 概念
SFINAE(Substitution Failure Is Not An Error)是 C++ 模板的核心机制之一。当编译器在推导模板参数时,如果某个替换导致无效代码,它不会报错,而是忽略这个候选,继续尝试其他重载。
Java 没有等价机制。Java 的方法重载在编译时选择最具体的签名,没有"替换失败"的概念。
| 特性 | Java 重载解析 | C++ SFINAE |
|---|---|---|
| 匹配失败时 | 编译错误 | 静默忽略该候选 |
| 约束表达 | 方法签名 + 类型边界 | enable_if / void_t / Concepts |
| 编译错误 | 明确 | 模板实例化栈( notoriously 难读) |
| 替代方案 | 无(Java 8+ 可用方法引用过滤) | C++20 Concepts(更清晰的错误信息) |
3.2.2 Type Traits
<type_traits> 是 C++ 标准库提供的编译期类型查询和变换工具。类似 Java 的反射(Class<T>),但完全在编译期执行。
| Java 反射 | C++ Type Traits |
|---|---|
T.class.isPrimitive() | std::is_integral_v<T> |
T.class.isArray() | std::is_array_v<T> |
Number.class.isAssignableFrom(T.class) | std::is_arithmetic_v<T> |
T.class.isEnum() | std::is_enum_v<T> |
T.class.isInterface() | std::is_class_v<T> |
Arrays.class.getComponentType() | std::remove_extent_t<T> |
| 无直接等价 | std::add_pointer_t<T> |
| 无直接等价 | std::decay_t<T> |
| 无直接等价 | std::conditional_t<Cond, T, F> |
3.2.3 模板特化与偏特化
| 特性 | Java | C++ |
|---|---|---|
| 方法重载 | 按签名选择 | 按签名选择 |
| 类型特化 | 不支持 | 支持(全特化 + 偏特化) |
| 运行时分派 | 虚方法 / instanceof | if constexpr / 特化 |
3.2.4 CRTP — 奇异递归模板模式
CRTP (Curiously Recurring Template Pattern) 是 C++ 中实现静态多态的经典模式。它用模板替代虚函数,在编译期完成分派,避免虚函数表开销。
对比 Java:Java 的模板方法模式(Template Method Pattern)依赖虚函数实现运行时多态。CRTP 在编译期实现同样的效果,零运行时开销。
3.2.5 完整 Demo
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
#include <concepts>
#include <sstream>
// ==================== SFINAE 示例 ====================
// 方式1:enable_if(C++11 风格)
// 只对算术类型启用
template<typename T>
std::enable_if_t<std::is_arithmetic_v<T>, T>
safe_divide(T a, T b) {
if (b == 0) throw std::invalid_argument("Division by zero");
return a / b;
}
// 只对字符串类型启用
template<typename T>
std::enable_if_t<std::is_same_v<std::decay_t<T>, std::string>, std::string>
safe_divide(T a, T b) {
// 字符串"除法":取前半部分
std::string result = a;
result.erase(result.size() / 2);
return result;
}
// 方式2:SFINAE + void_t 检测类型是否存在某个成员
template<typename, typename = void>
struct has_size : std::false_type {};
template<typename T>
struct has_size<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};
template<typename T>
inline constexpr bool has_size_v = has_size<T>::value;
// 方式3:if constexpr(C++17,推荐,最清晰)
template<typename T>
std::string describe(const T& value) {
std::ostringstream oss;
if constexpr (std::is_integral_v<T>) {
oss << "整数: " << value;
} else if constexpr (std::is_floating_point_v<T>) {
oss << "浮点数: " << value;
} else if constexpr (std::is_same_v<std::decay_t<T>, std::string>) {
oss << "字符串: \"" << value << "\"";
} else if constexpr (has_size_v<T>) {
oss << "容器(大小=" << value.size() << ")";
} else {
oss << "其他类型";
}
return oss.str();
}
// ==================== Type Traits 示例 ====================
template<typename T>
void print_type_info() {
std::cout << " is_integral: " << std::is_integral_v<T> << "\n";
std::cout << " is_floating_point: " << std::is_floating_point_v<T> << "\n";
std::cout << " is_arithmetic: " << std::is_arithmetic_v<T> << "\n";
std::cout << " is_pointer: " << std::is_pointer_v<T> << "\n";
std::cout << " is_reference: " << std::is_reference_v<T> << "\n";
std::cout << " is_const: " << std::is_const_v<T> << "\n";
std::cout << " is_array: " << std::is_array_v<T> << "\n";
std::cout << " is_class: " << std::is_class_v<T> << "\n";
std::cout << " is_enum: " << std::is_enum_v<T> << "\n";
std::cout << " size: " << sizeof(T) << " bytes\n";
}
// 类型变换示例
template<typename T>
using clean_type = std::remove_cv_t<std::remove_reference_t<std::decay_t<T>>>;
// 条件类型选择
template<typename T>
using storage_type = std::conditional_t<
(sizeof(T) <= 4),
int32_t,
int64_t
>;
// ==================== 模板特化 ====================
// 主模板
template<typename T>
struct Serializer {
static std::string serialize(const T& value) {
std::ostringstream oss;
oss << value;
return oss.str();
}
};
// 全特化:bool
template<>
struct Serializer<bool> {
static std::string serialize(bool value) {
return value ? "true" : "false";
}
};
// 全特化:std::vector<T>
template<typename T>
struct Serializer<std::vector<T>> {
static std::string serialize(const std::vector<T>& vec) {
std::ostringstream oss;
oss << "[";
for (std::size_t i = 0; i < vec.size(); ++i) {
if (i > 0) oss << ", ";
oss << Serializer<T>::serialize(vec[i]);
}
oss << "]";
return oss.str();
}
};
// ==================== CRTP 示例 ====================
// CRTP 基类:提供通用的计数和打印功能
template<typename Derived>
class Counter {
public:
void increment() {
++count_;
}
int count() const { return count_; }
void print_count() const {
// 编译期绑定到 Derived::name(),零虚函数开销
std::cout << static_cast<const Derived*>(this)->name()
<< " count: " << count_ << "\n";
}
protected:
int count_ = 0;
};
// CRTP 派生类
class Widget : public Counter<Widget> {
public:
std::string name() const { return "Widget"; }
void do_something() {
increment();
// 可以直接访问基类的 count_
std::cout << "Widget doing something (total: " << count_ << ")\n";
}
};
class Gadget : public Counter<Gadget> {
public:
std::string name() const { return "Gadget"; }
void do_something() {
increment();
std::cout << "Gadget doing something (total: " << count_ << ")\n";
}
};
// CRTP 基类:提供接口混入(Mixin)
template<typename Derived>
class Printable {
public:
std::string to_string() const {
const auto& self = static_cast<const Derived&>(*this);
std::ostringstream oss;
oss << self.class_name() << "(";
self.print_fields(oss);
oss << ")";
return oss.str();
}
void print() const {
std::cout << to_string() << "\n";
}
};
class Point : public Printable<Point> {
public:
Point(double x, double y) : x_(x), y_(y) {}
std::string class_name() const { return "Point"; }
void print_fields(std::ostringstream& oss) const {
oss << x_ << ", " << y_;
}
double x() const { return x_; }
double y() const { return y_; }
};
class Rectangle : public Printable<Rectangle> {
public:
Rectangle(double w, double h) : w_(w), h_(h) {}
std::string class_name() const { return "Rectangle"; }
void print_fields(std::ostringstream& oss) const {
oss << w_ << "x" << h_;
}
private:
double w_, h_;
};
// ==================== main ====================
int main() {
std::cout << "========== 模板进阶示例 ==========\n\n";
// --- SFINAE ---
std::cout << "--- SFINAE ---\n";
std::cout << "safe_divide(10, 3) = " << safe_divide(10, 3) << "\n";
std::cout << "safe_divide(10.0, 3.0) = " << safe_divide(10.0, 3.0) << "\n";
std::cout << "safe_divide(string) = " << safe_divide(std::string("HelloWorld"), std::string("unused")) << "\n";
std::cout << "\n--- describe (if constexpr) ---\n";
std::cout << "describe(42): " << describe(42) << "\n";
std::cout << "describe(3.14): " << describe(3.14) << "\n";
std::cout << "describe(\"hello\"): " << describe(std::string("hello")) << "\n";
std::vector<int> v{1, 2, 3};
std::cout << "describe(vector): " << describe(v) << "\n";
std::cout << "\nhas_size<vector<int>>: " << has_size_v<std::vector<int>> << "\n";
std::cout << "has_size<int>: " << has_size_v<int> << "\n";
// --- Type Traits ---
std::cout << "\n--- Type Traits ---\n";
std::cout << "int:\n"; print_type_info<int>();
std::cout << "double:\n"; print_type_info<double>();
std::cout << "const int&:\n"; print_type_info<const int&>();
std::cout << "int[10]:\n"; print_type_info<int[10]>();
std::cout << "\nstorage_type<char>: " << sizeof(storage_type<char>) << " bytes\n";
std::cout << "storage_type<double>: " << sizeof(storage_type<double>) << " bytes\n";
// --- 模板特化 ---
std::cout << "\n--- 模板特化 ---\n";
std::cout << "serialize(42): " << Serializer<int>::serialize(42) << "\n";
std::cout << "serialize(true): " << Serializer<bool>::serialize(true) << "\n";
std::cout << "serialize(3.14): " << Serializer<double>::serialize(3.14) << "\n";
std::vector<int> vec{1, 2, 3, 4, 5};
std::cout << "serialize(vector): " << Serializer<std::vector<int>>::serialize(vec) << "\n";
// --- CRTP ---
std::cout << "\n--- CRTP: 静态多态 ---\n";
Widget w;
Gadget g;
w.do_something();
w.do_something();
w.print_count();
g.do_something();
g.print_count();
std::cout << "\n--- CRTP: Mixin ---\n";
Point p{3.0, 4.0};
Rectangle r{10.0, 5.0};
p.print();
r.print();
return 0;
}
3.3 Concepts (C++20) [L4]
3.3.1 为什么需要 Concepts
SFINAE 的错误信息以难以阅读著称。C++20 引入 Concepts,为模板参数提供具名的约束,带来三个好处:
- 更清晰的错误信息:约束不满足时,编译器告诉你哪个 Concept 不满足,而不是一堆模板实例化栈。
- 更好的文档:Concept 名称本身就是文档。
- 约束重载:基于 Concept 做重载,比 SFINAE 更清晰。
3.3.2 与 Java 泛型约束对比
| 特性 | Java <T extends Number> | C++20 Concept |
|---|---|---|
| 语法 | <T extends Number> | requires std::integral<T> |
| 多约束 | <T extends Number & Comparable<T>> | requires std::integral<T> && std::totally_ordered<T> |
| 约束定义 | 接口/抽象类 | template<typename T> concept Name = ...; |
| 运行时 | 运行时类型检查 | 纯编译期 |
| 错误信息 | 清晰 | C++20 后大幅改善 |
| 约束位置 | 类型参数声明处 | requires 子句 / 简写 Concept auto |
| duck typing | 不支持 | 支持(只需满足语法要求) |
| auto 约束 | 不支持 | std::integral auto x = 42; |
3.3.3 标准库 Concepts 速查
| Concept | 含义 | Java 等价 |
|---|---|---|
std::integral<T> | 整数类型 | T instanceof Integer/Long/... |
std::floating_point<T> | 浮点类型 | T instanceof Double/Float |
std::signed_integral<T> | 有符号整数 | 类似 |
std::unsigned_integral<T> | 无符号整数 | 类似 |
std::same_as<T, U> | 相同类型 | T == U.class |
std::derived_from<D, B> | 继承关系 | D extends B |
std::convertible_to<From, To> | 可隐式转换 | 赋值兼容 |
std::totally_ordered<T> | 支持 < > <= >= | Comparable<T> |
std::invocable<F, Args...> | 可调用 | 函数式接口 |
std::predicate<F, Args...> | 返回 bool 的可调用 | Predicate<T> |
std::default_initializable<T> | 可默认构造 | 无参构造函数 |
3.3.4 完整 Demo
#include <iostream>
#include <string>
#include <vector>
#include <concepts>
#include <sstream>
#include <algorithm>
#include <ranges>
// ==================== 自定义 Concepts ====================
// Concept: 可哈希(有 std::hash 特化)
template<typename T>
concept Hashable = requires(T t) {
{ std::hash<T>{}(t) } -> std::convertible_to<std::size_t>;
};
// Concept: 可序列化为字符串
template<typename T>
concept Serializable = requires(const T& t, std::ostringstream& oss) {
{ oss << t } -> std::same_as<std::ostringstream&>;
};
// Concept: 可累加(支持 + 运算和初始值)
template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>;
};
// Concept: 容器(有 begin/end/size)
template<typename T>
concept Container = requires(T t) {
{ t.begin() } -> std::input_or_output_iterator;
{ t.end() } -> std::sentinel_for<decltype(t.begin())>;
{ t.size() } -> std::convertible_to<std::size_t>;
};
// Concept: 数值处理器(组合约束)
template<typename T>
concept NumericProcessor = std::integral<T> || std::floating_point<T>;
// Concept: 可打印(组合多个约束)
template<typename T>
concept Printable = requires(const T& t) {
{ std::cout << t } -> std::same_as<std::ostream&>;
} && Serializable<T>;
// ==================== 约束函数 ====================
// 方式1:requires 子句
template<typename T>
requires std::integral<T>
T clamp_int(T value, T lo, T hi) {
return value < lo ? lo : (value > hi ? hi : value);
}
// 方式2:简写语法(trailing requires)
template<std::integral T>
T clamp_integral(T value, T lo, T hi) {
return value < lo ? lo : (value > hi ? hi : value);
}
// 方式3:auto + Concept
Addable auto add(Addable auto a, Addable auto b) {
return a + b;
}
// 约束重载:根据 Concept 选择不同实现
// 对整数类型:快速路径
template<std::integral T>
std::string process(T value) {
return "整数处理: " + std::to_string(value);
}
// 对浮点类型:精度处理
template<std::floating_point T>
std::string process(T value) {
std::ostringstream oss;
oss << "浮点处理: " << std::fixed << value;
return oss.str();
}
// 对容器:批量处理
template<Container T>
std::string process(const T& container) {
std::ostringstream oss;
oss << "容器处理(大小=" << container.size() << "): [";
bool first = true;
for (const auto& item : container) {
if (!first) oss << ", ";
oss << item;
first = false;
}
oss << "]";
return oss.str();
}
// ==================== 约束类 ====================
// 约束类模板
template<NumericProcessor T>
class Accumulator {
public:
void add(T value) { sum_ += value; }
T sum() const { return sum_; }
void reset() { sum_ = T{}; }
private:
T sum_{};
};
// 约束成员函数
template<typename T>
class DataStore {
public:
// 只有当 T 是 Printable 时,才提供 print 方法
void print() const requires Printable<T> {
for (const auto& item : data_) {
std::cout << item << " ";
}
std::cout << "\n";
}
void push(const T& value) { data_.push_back(value); }
std::size_t size() const { return data_.size(); }
private:
std::vector<T> data_;
};
// ==================== Concept 组合与 requires 表达式 ====================
// 复杂约束:可排序容器
template<typename C>
concept SortableContainer = Container<C> && requires(C c) {
{ c.begin() } -> std::random_access_iterator;
{ std::sort(c.begin(), c.end()) }; // 可以被 sort
};
// requires 表达式:检查特定操作
template<typename T>
concept HasArea = requires(T t) {
{ t.area() } -> std::convertible_to<double>;
{ t.perimeter() } -> std::convertible_to<double>;
};
// 使用 HasArea Concept 的泛型函数
template<HasArea T>
double efficiency_ratio(const T& shape) {
double a = shape.area();
double p = shape.perimeter();
return (p > 0) ? (a / p) : 0.0;
}
// ==================== 测试用的形状类 ====================
class Circle {
public:
explicit Circle(double r) : radius_(r) {}
double area() const { return 3.14159265 * radius_ * radius_; }
double perimeter() const { return 2 * 3.14159265 * radius_; }
private:
double radius_;
};
class Square {
public:
explicit Square(double side) : side_(side) {}
double area() const { return side_ * side_; }
double perimeter() const { return 4 * side_; }
private:
double side_;
};
// ==================== main ====================
int main() {
std::cout << "========== Concepts (C++20) 示例 ==========\n\n";
// --- 约束函数 ---
std::cout << "--- 约束函数 ---\n";
std::cout << "clamp_int(150, 0, 100): " << clamp_int(150, 0, 100) << "\n";
std::cout << "clamp_integral(-5, 0, 100): " << clamp_integral(-5, 0, 100) << "\n";
std::cout << "add(3, 4): " << add(3, 4) << "\n";
std::cout << "add(1.5, 2.5): " << add(1.5, 2.5) << "\n";
// --- 约束重载 ---
std::cout << "\n--- 约束重载 ---\n";
std::cout << "process(int): " << process(42) << "\n";
std::cout << "process(double): " << process(3.14) << "\n";
std::cout << "process(vector): " << process(std::vector<int>{1, 2, 3}) << "\n";
// --- 约束类 ---
std::cout << "\n--- 约束类 ---\n";
Accumulator<int> int_acc;
int_acc.add(10);
int_acc.add(20);
int_acc.add(30);
std::cout << "Accumulator<int> sum: " << int_acc.sum() << "\n";
Accumulator<double> double_acc;
double_acc.add(1.5);
double_acc.add(2.5);
std::cout << "Accumulator<double> sum: " << double_acc.sum() << "\n";
// DataStore 的条件成员函数
DataStore<int> int_store;
int_store.push(1);
int_store.push(2);
int_store.push(3);
std::cout << "DataStore<int> print: ";
int_store.print();
// --- Concept 检查 ---
std::cout << "\n--- Concept 检查 ---\n";
std::cout << "std::integral<int>: " << std::integral<int> << "\n";
std::cout << "std::integral<double>: " << std::integral<double> << "\n";
std::cout << "std::floating_point<double>: " << std::floating_point<double> << "\n";
std::cout << "Addable<int>: " << Addable<int> << "\n";
std::cout << "Addable<std::string>: " << Addable<std::string> << "\n";
std::cout << "Container<std::vector<int>>: " << Container<std::vector<int>> << "\n";
std::cout << "Hashable<int>: " << Hashable<int> << "\n";
std::cout << "Hashable<std::vector<int>>: " << Hashable<std::vector<int>> << "\n";
// --- HasArea Concept ---
std::cout << "\n--- HasArea Concept ---\n";
Circle c{5.0};
Square s{4.0};
std::cout << "Circle efficiency: " << efficiency_ratio(c) << "\n";
std::cout << "Square efficiency: " << efficiency_ratio(s) << "\n";
return 0;
}
3.4 常量表达式与编译期计算 [L3]
3.4.1 constexpr / consteval / constinit 对比
| 关键字 | 引入版本 | 含义 | Java 等价 |
|---|---|---|---|
constexpr | C++11 | "如果可能,在编译期求值" | static final(部分等价) |
consteval | C++20 | "必须在编译期求值"(立即函数) | 无直接等价(Java 注解处理器?) |
constinit | C++20 | "编译期初始化,但运行时可变" | 无直接等价 |
const | C | "运行期不可变" | final |
核心区别:
constexpr函数:可以在编译期执行,但如果参数不是常量,也可以在运行期执行。consteval函数:必须在编译期执行,参数不是常量则编译错误。constinit变量:保证在编译期初始化(避免静态初始化顺序问题),但之后可以修改。const变量:初始化后不可修改(但初始化可能在运行期)。
3.4.2 if constexpr
if constexpr 在编译期根据条件选择分支,被丢弃的分支甚至不需要编译通过。这是 C++ 模板元编程的重大改进。
对比 Java:Java 没有等价机制。Java 的泛型方法中,你不能写 if (T instanceof Integer) 并在分支中使用 T 的整数方法(类型擦除后 T 就是 Object)。
3.4.3 编译期字符串处理
C++ 允许在编译期处理字符串,这是 Java 完全不具备的能力。典型应用:编译期字符串哈希(用于高效字符串比较、switch-case 优化)。
3.4.4 完整 Demo
#include <iostream>
#include <string_view>
#include <array>
#include <cstdint>
// ==================== constexpr 基础 ====================
// constexpr 函数:编译期或运行期均可调用
constexpr int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// constexpr 变量:编译期常量
constexpr int FACT_5 = factorial(5); // 编译期计算
constexpr int FACT_10 = factorial(10);
// ==================== consteval:强制编译期 ====================
// consteval 函数:必须在编译期调用
consteval int square(int n) {
return n * n;
}
// consteval 不能接受运行期参数
// int runtime_val = 10;
// square(runtime_val); // 编译错误!
// ==================== constinit:编译期初始化 ====================
// constinit 保证全局变量在编译期初始化
// 避免静态初始化顺序问题(Static Initialization Order Fiasco)
constinit int global_counter = 0;
struct Config {
int max_size;
double threshold;
};
// constinit 保证 Config 在编译期初始化
constinit Config global_config = {1024, 0.95};
// ==================== constexpr 斐波那契 ====================
// 递归版本(简洁,但编译期栈深度有限)
constexpr int fib_recursive(int n) {
if (n <= 1) return n;
return fib_recursive(n - 1) + fib_recursive(n - 2);
}
// 迭代版本(编译期友好,无栈溢出风险)
constexpr int fib_iterative(int n) {
if (n <= 1) return n;
int a = 0, b = 1;
for (int i = 2; i <= n; ++i) {
int tmp = a + b;
a = b;
b = tmp;
}
return b;
}
// 编译期生成斐波那契查找表
template<int N>
constexpr auto make_fib_table() {
std::array<int, N> table{};
for (int i = 0; i < N; ++i) {
table[i] = fib_iterative(i);
}
return table;
}
// 编译期斐波那契表
constexpr auto FIB_TABLE = make_fib_table<20>();
// ==================== if constexpr ====================
// 编译期类型分派
template<typename T>
std::string type_name() {
if constexpr (std::is_same_v<T, int>) {
return "int";
} else if constexpr (std::is_same_v<T, double>) {
return "double";
} else if constexpr (std::is_same_v<T, float>) {
return "float";
} else if constexpr (std::is_same_v<T, char>) {
return "char";
} else if constexpr (std::is_same_v<T, bool>) {
return "bool";
} else if constexpr (std::is_pointer_v<T>) {
return type_name<std::remove_pointer_t<T>>() + "*";
} else {
return "unknown";
}
}
// 编译期选择算法
template<typename T>
constexpr T compute(T value) {
if constexpr (std::is_integral_v<T>) {
// 整数:位运算
return value * 2 + 1;
} else if constexpr (std::is_floating_point_v<T>) {
// 浮点:数学运算
return value * 3.14159;
} else {
// 其他:直接返回
return value;
}
}
// 编译期判断大小端
constexpr bool is_little_endian() {
constexpr int test_val = 1;
// 通过 reinterpret_cast 在 constexpr 中检查字节序
const auto bytes = reinterpret_cast<const unsigned char*>(&test_val);
return bytes[0] == 1;
}
// ==================== 编译期字符串哈希 ====================
// FNV-1a 哈希算法(编译期执行)
consteval uint64_t fnv1a_hash(std::string_view str) {
uint64_t hash = 14695981039346656037ULL; // FNV offset basis
for (char c : str) {
hash ^= static_cast<uint64_t>(static_cast<unsigned char>(c));
hash *= 1099511628211ULL; // FNV prime
}
return hash;
}
// 编译期字符串哈希常量
consteval uint64_t operator""_hash(const char* str, std::size_t len) {
return fnv1a_hash(std::string_view{str, len});
}
// 编译期字符串反转
constexpr std::string_view reverse_view(std::string_view sv) {
// string_view 不可变,这里只做演示
return sv;
}
// 编译期字符串长度
constexpr std::size_t str_len(const char* str) {
std::size_t len = 0;
while (str[len] != '\0') ++len;
return len;
}
// 编译期字符串是否是回文
constexpr bool is_palindrome(std::string_view sv) {
std::size_t left = 0;
std::size_t right = sv.size();
if (right == 0) return true;
while (left < right - 1) {
if (sv[left] != sv[right - 1]) return false;
++left;
--right;
}
return true;
}
// 编译期字符串查找
constexpr std::size_t find_char(std::string_view sv, char c) {
for (std::size_t i = 0; i < sv.size(); ++i) {
if (sv[i] == c) return i;
}
return std::string_view::npos;
}
// ==================== 编译期数据结构 ====================
// 编译期链表节点
template<int Value, typename Next = void>
struct IntList {
static constexpr int value = Value;
using next = Next;
};
// 编译期链表求和
template<typename List>
struct SumList;
template<>
struct SumList<void> {
static constexpr int value = 0;
};
template<int V, typename N>
struct SumList<IntList<V, N>> {
static constexpr int value = V + SumList<N>::value;
};
// 构建编译期链表
using MyList = IntList<1, IntList<2, IntList<3, IntList<4, IntList<5, void>>>>>;
// ==================== main ====================
int main() {
std::cout << "========== 常量表达式与编译期计算 ==========\n\n";
// --- constexpr 基础 ---
std::cout << "--- constexpr ---\n";
std::cout << "5! = " << FACT_5 << " (编译期计算)\n";
std::cout << "10! = " << FACT_10 << " (编译期计算)\n";
// 运行期也可以调用 constexpr 函数
int n = 6;
std::cout << n << "! = " << factorial(n) << " (运行期计算)\n";
// --- consteval ---
std::cout << "\n--- consteval ---\n";
std::cout << "square(7) = " << square(7) << " (编译期)\n";
std::cout << "square(12) = " << square(12) << " (编译期)\n";
// --- constinit ---
std::cout << "\n--- constinit ---\n";
std::cout << "global_config.max_size = " << global_config.max_size << "\n";
std::cout << "global_config.threshold = " << global_config.threshold << "\n";
// constinit 变量可以修改
global_config.max_size = 2048;
std::cout << "修改后 global_config.max_size = " << global_config.max_size << "\n";
// --- 斐波那契 ---
std::cout << "\n--- constexpr 斐波那契 ---\n";
std::cout << "fib(10) 递归: " << fib_recursive(10) << "\n";
std::cout << "fib(10) 迭代: " << fib_iterative(10) << "\n";
std::cout << "fib(20) 迭代: " << fib_iterative(20) << "\n";
std::cout << "编译期斐波那契表: ";
for (int i = 0; i < 20; ++i) {
std::cout << FIB_TABLE[i];
if (i < 19) std::cout << ", ";
}
std::cout << "\n";
// --- if constexpr ---
std::cout << "\n--- if constexpr ---\n";
std::cout << "type_name<int>: " << type_name<int>() << "\n";
std::cout << "type_name<double>: " << type_name<double>() << "\n";
std::cout << "type_name<int*>: " << type_name<int*>() << "\n";
std::cout << "type_name<char>: " << type_name<char>() << "\n";
std::cout << "compute(5): " << compute(5) << "\n";
std::cout << "compute(2.0): " << compute(2.0) << "\n";
std::cout << "is_little_endian: " << is_little_endian() << "\n";
// --- 编译期字符串哈希 ---
std::cout << "\n--- 编译期字符串哈希 ---\n";
std::cout << "hash(\"hello\"): " << fnv1a_hash("hello") << "\n";
std::cout << "hash(\"world\"): " << fnv1a_hash("world") << "\n";
std::cout << "hash(\"hello\") == \"hello\"_hash: "
<< (fnv1a_hash("hello") == "hello"_hash) << "\n";
// 编译期字符串操作
std::cout << "\n--- 编译期字符串操作 ---\n";
constexpr bool pal1 = is_palindrome("racecar");
constexpr bool pal2 = is_palindrome("hello");
std::cout << "is_palindrome(\"racecar\"): " << pal1 << "\n";
std::cout << "is_palindrome(\"hello\"): " << pal2 << "\n";
constexpr auto pos = find_char("hello world", 'w');
std::cout << "find_char(\"hello world\", 'w'): " << pos << "\n";
constexpr auto len = str_len("constexpr");
std::cout << "str_len(\"constexpr\"): " << len << "\n";
// --- 编译期数据结构 ---
std::cout << "\n--- 编译期链表 ---\n";
std::cout << "SumList<1,2,3,4,5>: " << SumList<MyList>::value << "\n";
return 0;
}
编译说明
所有示例使用 C++23 标准编译:
# 单个文件编译
g++ -std=c++23 -Wall -Wextra -O2 demo.cpp -o demo && ./demo
# 如果编译器不支持 C++23,退回 C++20
g++ -std=c++20 -Wall -Wextra -O2 demo.cpp -o demo && ./demo
关键要点总结
| 主题 | Java/Kotlin 开发者需要理解的 |
|---|---|
| 模板 vs 泛型 | C++ 模板是编译期代码生成,不是类型擦除。每种类型生成独立代码,零开销但有代码膨胀 |
| CTAD | 类似 Java 钻石操作符 <>,但能力更强——真正从参数推导类型 |
| 变参模板 | Java 无等价物。允许任意数量、任意类型的模板参数 |
| 非类型参数 | Java 无等价物。可以用值(不仅是类型)参数化模板 |
| SFINAE | Java 无等价物。替换失败不是错误,用于约束模板 |
| Type Traits | 类似 Java 反射,但完全在编译期执行 |
| CRTP | 用模板替代虚函数实现静态多态,零运行时开销 |
| Concepts | C++20 的类型约束,类似 <T extends Number> 但更强大,支持 duck typing |
| constexpr | 编译期计算能力远超 Java。Java 的 static final 只是常量,不能做编译期计算 |
| if constexpr | 编译期条件分支,被丢弃的分支不需要编译通过。Java 无等价物 |