3. 第三部分:类型系统与泛型

0 阅读25分钟

第三部分:类型系统与泛型

[!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/KotlinC++
声明语法<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 模板特化与偏特化

特性JavaC++
方法重载按签名选择按签名选择
类型特化不支持支持(全特化 + 偏特化)
运行时分派虚方法 / instanceofif 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,为模板参数提供具名的约束,带来三个好处:

  1. 更清晰的错误信息:约束不满足时,编译器告诉你哪个 Concept 不满足,而不是一堆模板实例化栈。
  2. 更好的文档:Concept 名称本身就是文档。
  3. 约束重载:基于 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 等价
constexprC++11"如果可能,在编译期求值"static final(部分等价)
constevalC++20"必须在编译期求值"(立即函数)无直接等价(Java 注解处理器?)
constinitC++20"编译期初始化,但运行时可变"无直接等价
constC"运行期不可变"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 无等价物。可以用值(不仅是类型)参数化模板
SFINAEJava 无等价物。替换失败不是错误,用于约束模板
Type Traits类似 Java 反射,但完全在编译期执行
CRTP用模板替代虚函数实现静态多态,零运行时开销
ConceptsC++20 的类型约束,类似 <T extends Number> 但更强大,支持 duck typing
constexpr编译期计算能力远超 Java。Java 的 static final 只是常量,不能做编译期计算
if constexpr编译期条件分支,被丢弃的分支不需要编译通过。Java 无等价物