【C++】模板与特化技术全面教程(claude sonnet 4)

article/2025/8/4 10:01:52



第一章:模板的基础概念 (Template Fundamentals)

1.1 什么是模板?

模板 (Template) 是C++中的一种泛型编程 (Generic Programming) 机制,它允许我们编写与类型无关的代码。想象一下,如果我们要为不同的数据类型编写相同逻辑的函数,没有模板的话,我们需要为每种类型都写一遍代码。

模板的核心思想是代码复用 (Code Reuse) 和类型安全 (Type Safety)。编译器会根据实际使用的类型自动生成相应的代码,这个过程称为模板实例化 (Template Instantiation)。

1.2 模板的基本语法

// 基本模板语法结构template<typename T>  // 或者使用 template<class T>返回类型 函数名(参数列表) {    // 函数体}

注意typename 和 class 在这里是等价的,但现代C++推荐使用 typename,因为它更清晰地表达了T是一个类型参数。

第二章:函数模板 (Function Templates)

2.1 简单函数模板

让我们从一个简单的例子开始理解函数模板:

#include <iostream>
// 找出两个值中的最大值的模板函数template<typename T>T max_value(T a, T b) {    return (a > b) ? a : b;}
int main() {    // 编译器会自动推导类型 (Type Deduction)    std::cout << max_value(10, 20) << std::endl;        // T = int    std::cout << max_value(3.14, 2.71) << std::endl;    // T = double    std::cout << max_value('a', 'z') << std::endl;      // T = char// 也可以显式指定类型 (Explicit Type Specification)    std::cout << max_value<int>(10.5, 20.7) << std::endl;  // 强制转换为intreturn 0;}

2.2 多参数模板

模板可以有多个类型参数:

// 多类型参数的模板函数template<typename T1, typename T2>auto add_different_types(T1 a, T2 b) -> decltype(a + b) {    return a + b;}
// C++14及以后可以简化为:template<typename T1, typename T2>auto add_different_types_v2(T1 a, T2 b) {    return a + b;  // 编译器自动推导返回类型}


2.3 非类型模板参数 (Non-type Template Parameters)

除了类型参数,模板还可以接受非类型参数:

#include <array>// 创建固定大小数组并初始化的模板函数template<typename T, std::size_t N>std::array<T, N> create_array(const T& initial_value) {    std::array<T, N> arr;    arr.fill(initial_value);    return arr;}int main() {    auto int_array = create_array<int, 5>(42);    // 创建5个元素的int数组,都初始化为42    auto double_array = create_array<double, 3>(3.14);  // 创建3个元素的double数组    return 0;}


第三章:类模板 (Class Templates)

3.1 基本类模板

类模板允许我们创建泛型类。让我们创建一个简单的栈 (Stack) 类:

#include <vector>#include <stdexcept>
template<typename T>class Stack {private:    std::vector<T> elements;  // 使用vector存储元素
public:    // 入栈操作    void push(const T& element) {        elements.push_back(element);    }// 出栈操作    T pop() {        if (elements.empty()) {            throw std::runtime_error("Stack is empty");        }        T result = elements.back();        elements.pop_back();        return result;    }// 查看栈顶元素    const T& top() const {        if (elements.empty()) {            throw std::runtime_error("Stack is empty");        }        return elements.back();    }// 检查栈是否为空    bool empty() const {        return elements.empty();    }// 获取栈的大小    std::size_t size() const {        return elements.size();    }};
// 使用示例int main() {    Stack<int> int_stack;        // 整数栈    Stack<std::string> str_stack; // 字符串栈int_stack.push(10);    int_stack.push(20);str_stack.push("Hello");    str_stack.push("World");std::cout << int_stack.pop() << std::endl;  // 输出: 20    std::cout << str_stack.pop() << std::endl;  // 输出: Worldreturn 0;}


3.2 模板类的成员函数定义

有时我们需要将类模板的成员函数定义在类外部:

template<typename T>class Matrix {private:    std::vector<std::vector<T>> data;    std::size_t rows, cols;
public:    Matrix(std::size_t r, std::size_t c);  // 构造函数声明    T& at(std::size_t row, std::size_t col);  // 元素访问函数声明    void print() const;  // 打印函数声明};
// 构造函数定义(注意模板语法)template<typename T>Matrix<T>::Matrix(std::size_t r, std::size_t c)     : rows(r), cols(c), data(r, std::vector<T>(c)) {    // 初始化矩阵}
// 成员函数定义template<typename T>T& Matrix<T>::at(std::size_t row, std::size_t col) {    if (row >= rows || col >= cols) {        throw std::out_of_range("Matrix index out of range");    }    return data[row][col];}
template<typename T>void Matrix<T>::print() const {    for (const auto& row : data) {        for (const auto& element : row) {            std::cout << element << " ";        }        std::cout << std::endl;    }}


第四章:模板特化 (Template Specialization)

4.1 全特化 (Full Specialization)

有时候,我们需要为特定的类型提供特殊的实现。这就是模板特化的用处:

#include <iostream>#include <string>
// 通用模板函数template<typename T>void process_data(const T& data) {    std::cout << "处理通用数据: " << data << std::endl;}
// 为字符串类型提供特化版本template<>void process_data<std::string>(const std::string& data) {    std::cout << "处理字符串数据 (特化版本): \"" << data << "\" (长度: " << data.length() << ")" << std::endl;}
// 为指针类型提供特化版本template<>void process_data<int*>(int* const& data) {    if (data) {        std::cout << "处理指针数据 (特化版本): 地址=" << data << ", 值=" << *data << std::endl;    } else {        std::cout << "处理空指针" << std::endl;    }}
int main() {    process_data(42);           // 使用通用版本    process_data(3.14);         // 使用通用版本    process_data(std::string("Hello World"));  // 使用字符串特化版本int value = 100;    process_data(&value);       // 使用指针特化版本return 0;}


4.2 类模板的全特化

类模板也可以进行全特化:

#include <iostream>#include <cstring>// 通用的存储类模板template<typename T>class Storage {private:    T data;public:    Storage(const T& value) : data(value) {}    void print() const {        std::cout << "存储的数据: " << data << std::endl;    }    T get() const { return data; }};// 为const char*提供特化版本(处理C风格字符串)template<>class Storage<const char*> {private:    char* data;public:    Storage(const char* str) {        data = new char[std::strlen(str) + 1];        std::strcpy(data, str);    }    ~Storage() {        delete[] data;    }    // 复制构造函数    Storage(const Storage& other) {        data = new char[std::strlen(other.data) + 1];        std::strcpy(data, other.data);    }    // 赋值操作符    Storage& operator=(const Storage& other) {        if (this != &other) {            delete[] data;            data = new char[std::strlen(other.data) + 1];            std::strcpy(data, other.data);        }        return *this;    }    void print() const {        std::cout << "存储的C字符串 (特化版本): \"" << data << "\"" << std::endl;    }    const char* get() const { return data; }};int main() {    Storage<int> int_storage(42);    Storage<const char*> str_storage("Hello World");    int_storage.print();  // 使用通用版本    str_storage.print();  // 使用特化版本    return 0;}

4.3 偏特化 (Partial Specialization)

偏特化只能用于类模板,不能用于函数模板。它允许我们为模板参数的某些组合提供特殊实现:

#include <iostream>  // 包含标准输入输出库,用于后续的输入和输出操作
// 通用的配对类模板// 定义一个模板类 Pair,它能够存储一个配对数据,支持两种不同类型 T1 和 T2template<typename T1, typename T2>class Pair {private:    T1 first;   // 定义一个变量 first,类型为 T1,用来存储配对中的第一个元素    T2 second;  // 定义一个变量 second,类型为 T2,用来存储配对中的第二个元素public:    // 构造函数,使用常量引用传参,并利用初始化列表初始化成员变量    Pair(const T1& f, const T2& s) : first(f), second(s) {}// 成员函数 print,用于打印配对的内容(不修改成员变量,故为 const 成员函数)    void print() const {        // 输出格式为:通用配对: (first, second)        std::cout << "通用配对: (" << first << ", " << second << ")" << std::endl;    }};
// 偏特化:当两个类型相同时// 下面定义 Pair 的偏特化版本,当两个模板参数类型相同时(即 T1 和 T2 都是 T)使用此版本template<typename T>class Pair<T, T> {private:    T first;   // 定义变量 first,类型为 T    T second;  // 定义变量 second,类型为 Tpublic:    // 构造函数,同样使用初始化列表初始化两个成员变量    Pair(const T& f, const T& s) : first(f), second(s) {}// 重写 print 函数,以显示偏特化的信息    void print() const {        // 输出格式为:相同类型配对 (偏特化): (first, second)        std::cout << "相同类型配对 (偏特化): (" << first << ", " << second << ")" << std::endl;    }// 额外功能:检查两个值是否相等    // 定义一个成员函数 are_equal(),用于比较 first 和 second 是否相等,并返回布尔值    bool are_equal() const {         return first == second;    }};
// 偏特化:当第二个类型是指针时// 定义 Pair 的另一个偏特化版本,当第二个模板参数为指针类型 T2* 时使用此版本template<typename T1, typename T2>class Pair<T1, T2*> {private:    T1 first;      // 定义变量 first,类型为 T1    T2* second;    // 定义变量 second,类型为 T2*,即指针类型public:    // 构造函数,接受 T1 类型的值和 T2 指针,通过初始化列表初始化成员变量    Pair(const T1& f, T2* s) : first(f), second(s) {}// 重写 print 函数,专门处理指针类型的输出    void print() const {        // 输出起始部分:显示 "指针配对 (偏特化): (first, "        std::cout << "指针配对 (偏特化): (" << first << ", ";        // 判断指针 second 是否为空        if (second) {            // 如果 second 非空,输出指针所指向的数据(解引用)            std::cout << *second;        } else {            // 如果 second 为空,输出 "nullptr"            std::cout << "nullptr";        }        // 输出闭合括号并换行        std::cout << ")" << std::endl;    }};
int main() {    // 创建一个 Pair 对象 p1,模板参数为 <int, double>,调用通用版本构造函数    Pair<int, double> p1(1, 2.5);      // 使用通用版本:当类型不同    // 创建一个 Pair 对象 p2,模板参数为 <int, int>,因此调用相同类型的偏特化版本    Pair<int, int> p2(3, 4);           // 使用偏特化版本(相同类型)int value = 100;  // 定义一个 int 类型的变量 value,用于测试指针配对// 创建一个 Pair 对象 p3,模板参数为 <std::string, int*>,调用第二个偏特化版(指针类型)    Pair<std::string, int*> p3("数值", &value);  // 使用偏特化版本(指针类型)// 调用 p1 的 print 函数打印通用配对数据    p1.print();// 调用 p2 的 print 函数打印相同类型偏特化配对数据    p2.print();// 调用 p2 的 are_equal 函数检查 p2 中两个值是否相等,并将结果打印出来    std::cout << "p2中的值相等吗? " << (p2.are_equal() ? "是" : "否") << std::endl;// 调用 p3 的 print 函数打印指针配对数据,并正确显示指针指向的值    p3.print();return 0;  // 返回 0 表示程序正常结束}

第五章:高级模板技术

5.1 SFINAE (Substitution Failure Is Not An Error)

SFINAE是C++模板元编程中的一个重要概念。当模板参数替换失败时,编译器不会报错,而是简单地从重载候选中移除该模板:

#include <iostream>#include <type_traits>// 使用SFINAE检测类型是否有特定成员函数template<typename T>class has_size_method {private:    // 这个技巧用于检测T是否有size()方法    template<typename U>    static auto test(int) -> decltype(std::declval<U>().size(), std::true_type{});    template<typename>    static std::false_type test(...);public:    static constexpr bool value = decltype(test<T>(0))::value;};// 根据是否有size方法选择不同的实现template<typename T>typename std::enable_if<has_size_method<T>::value, std::size_t>::typeget_container_info(const T& container) {    std::cout << "容器有size方法,大小为: ";    return container.size();}template<typename T>typename std::enable_if<!has_size_method<T>::value, std::size_t>::typeget_container_info(const T& container) {    std::cout << "容器没有size方法,无法获取大小。返回: ";    return 0;}int main() {    std::vector<int> vec{1, 2, 3, 4, 5};    int arr[] = {1, 2, 3};    std::cout << get_container_info(vec) << std::endl;  // vector有size方法    std::cout << get_container_info(arr) << std::endl;  // 数组没有size方法    return 0;}

5.2 变参模板 (Variadic Templates)

C++11引入了变参模板,允许模板接受可变数量的参数:

#include <iostream>  // 包含标准输入输出流库,用于后续的输入输出操作
// 递归版本的变参模板函数// 定义处理只有一个参数的模板函数,当只剩下最后一个参数时调用该函数template<typename T>void print_values(const T& value) {    // 递归的基础情况:输出传入的单个参数并换行    std::cout << value << std::endl;}
// 定义处理多个参数的模板函数,利用变参模板实现递归调用template<typename T, typename... Args>void print_values(const T& first, const Args&... rest) {    // 输出第一个参数并在后面接一个空格    std::cout << first << " ";    // 递归调用print_values,将剩余参数传入    print_values(rest...);  // 递归调用,逐个处理剩余参数}
// C++17折叠表达式版本 (Fold Expression)// 定义使用折叠表达式实现的变参函数,一次性展开所有参数进行打印template<typename... Args>void print_values_cpp17(const Args&... args) {    // 利用折叠表达式对每个参数执行 std::cout << 参数 << " " 的操作    ((std::cout << args << " "), ...);    // 所有参数打印完成后输出换行符    std::cout << std::endl;}
// 计算多个值的和// 定义一个使用折叠表达式计算传入所有参数之和的模板函数template<typename... Args>auto sum(Args... args) {    // 利用折叠表达式 (args + ...) 计算所有参数累加的结果    return (args + ...);  // C++17折叠表达式,依次累加所有参数}
// 变参模板类:元组的简单实现// 声明模板类SimpleTuple,可以接收任意数量和类型的参数,用于保存一组数据template<typename... Types>class SimpleTuple;
// 特化:空元组// 当没有传入任何类型参数时,提供一个空特化版本,作为递归终止条件template<>class SimpleTuple<> {};
// 递归定义:头部+尾部// 当有至少一个参数时,将第一个元素作为头部,其余元素构成尾部元组template<typename Head, typename... Tail>class SimpleTuple<Head, Tail...> {private:    Head head;                  // 保存元组的第一个元素,即头部    SimpleTuple<Tail...> tail;    // 保存剩余的元素(尾部),以一个SimpleTuple表示public:    // 构造函数,使用初始化列表依次初始化头部元素和尾部元组    SimpleTuple(Head h, Tail... t) : head(h), tail(t...) {}// 返回头部元素的引用,可供外部访问和修改    Head& get_head() { return head; }    // 常量版本,返回头部元素的常量引用,保护数据不被修改    const Head& get_head() const { return head; }// 返回尾部(子元组)的引用,用于递归访问余下元素    SimpleTuple<Tail...>& get_tail() { return tail; }    // 常量版本,返回尾部(子元组)的常量引用    const SimpleTuple<Tail...>& get_tail() const { return tail; }};
int main() {    // 测试变参函数:使用递归版本打印多个参数    print_values(1, 2.5, "Hello", 'A');// 测试变参函数:使用 C++17 折叠表达式版本打印多个参数    print_values_cpp17(1, 2.5, "World", 'B');// 测试求和函数:依次累加整数和浮点数    std::cout << "1+2+3+4+5 = " << sum(1, 2, 3, 4, 5) << std::endl;    std::cout << "1.5+2.7+3.2 = " << sum(1.5, 2.7, 3.2) << std::endl;// 测试简单元组的实现    // 创建一个SimpleTuple对象,包含 int、double 和 std::string 三种类型的元素    SimpleTuple<int, double, std::string> tuple(42, 3.14, "Hello");    // 访问并打印元组中的头部元素    std::cout << "元组第一个元素: " << tuple.get_head() << std::endl;    return 0;  // 返回0表示程序正常结束}

第六章:模板元编程 (Template Metaprogramming)

6.1 编译期计算

模板可以在编译期进行计算,这是模板元编程的基础:

#include <iostream>  // 包含标准输入输出流库,用于后续在终端输出结果
// 编译期计算阶乘// 定义模板结构体 Factorial,通过模板递归在编译期计算阶乘的值template<int N>struct Factorial {    // 定义静态 constexpr 成员 value,其值为 N 乘以 Factorial<N - 1> 的值    static constexpr int value = N * Factorial<N - 1>::value;};
// 特化:递归终止条件// 当 N == 0 时,阶乘定义为1,此特化用于递归终止,防止无限递归template<>struct Factorial<0> {    // 定义静态 constexpr 成员 value,其值为 1    static constexpr int value = 1;};
// 编译期计算斐波那契数列// 定义模板结构体 Fibonacci,通过模板递归在编译期计算斐波那契数template<int N>struct Fibonacci {    // 定义静态 constexpr 成员 value,其值为 Fibonacci<N - 1> 与 Fibonacci<N - 2> 的和    static constexpr int value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;};
// 特化:递归终止条件(第一种情况)// 当 N == 0 时,斐波那契数值定义为 0,此特化用于递归终止template<>struct Fibonacci<0> {    // 定义静态 constexpr 成员 value,其值为 0    static constexpr int value = 0;};
// 特化:递归终止条件(第二种情况)// 当 N == 1 时,斐波那契数值定义为 1,此特化用于递归终止template<>struct Fibonacci<1> {    // 定义静态 constexpr 成员 value,其值为 1    static constexpr int value = 1;};
// 使用constexpr函数 (C++11及以后的现代方法)// 定义 constexpr 函数 factorial_func,通过递归在编译期或运行期计算阶乘constexpr int factorial_func(int n) {    // 如果 n 小于等于1,则返回1;否则返回 n 乘以 factorial_func(n - 1)    return (n <= 1) ? 1 : n * factorial_func(n - 1);}
// 定义 constexpr 函数 fibonacci_func,通过递归在编译期或运行期计算斐波那契数列的值constexpr int fibonacci_func(int n) {    // 如果 n 小于等于1,则直接返回 n;否则返回 fibonacci_func(n - 1) 与 fibonacci_func(n - 2) 的和    return (n <= 1) ? n : fibonacci_func(n - 1) + fibonacci_func(n - 2);}
int main() {    // 这些值在编译期就计算好了    // 通过模板方式计算5的阶乘,并将结果赋予 fact5,其值应为120    constexpr int fact5 = Factorial<5>::value;      // 120    // 通过模板方式计算第10个斐波那契数,并将结果赋予 fib10,其值应为55    constexpr int fib10 = Fibonacci<10>::value;       // 55    // 使用 constexpr 函数计算5的阶乘,赋值给 fact5_func,结果同样为120    constexpr int fact5_func = factorial_func(5);     // 120    // 使用 constexpr 函数计算第10个斐波那契数,赋值给 fib10_func,结果同样为55    constexpr int fib10_func = fibonacci_func(10);    // 55// 输出模板版本和 constexpr函数版本计算的结果    std::cout << "5! = " << fact5 << " (模板版本)" << std::endl;    std::cout << "5! = " << fact5_func << " (constexpr函数版本)" << std::endl;    std::cout << "fibonacci(10) = " << fib10 << " (模板版本)" << std::endl;    std::cout << "fibonacci(10) = " << fib10_func << " (constexpr函数版本)" << std::endl;return 0;  // 返回0表示程序正常结束}

6.2 类型萃取 (Type Traits)

类型萃取用于在编译期获取类型的信息:

#include <iostream>       // 包含输入输出流库,用于输出信息#include <type_traits>    // 包含标准类型萃取工具,比如 std::is_integral 等
// 自定义的类型萃取:检查是否是指针类型// 默认情况下,所有类型都不是指针,所以 value 为 falsetemplate<typename T>struct is_pointer {    static constexpr bool value = false;  // 非指针类型时,值为 false};
// 当类型为指针时(T*),特化版本将 value 设置为 truetemplate<typename T>struct is_pointer<T*> {    static constexpr bool value = true;   // 指针类型时,值为 true};
// 移除指针的类型萃取:如果类型是指针,则返回指针指向的类型;否则返回原类型// 默认情况,不是指针的类型,移除指针后仍为 Ttemplate<typename T>struct remove_pointer {    using type = T;};
// 特化版本:对于指针类型 T*,移除指针后得到的类型为 Ttemplate<typename T>struct remove_pointer<T*> {    using type = T;};
// 辅助模板别名 (C++14):简化 remove_pointer<T>::type 的写法template<typename T>using remove_pointer_t = typename remove_pointer<T>::type;
// 条件类型选择:根据布尔条件选择类型// 当 Condition 为 true 时,type 定义为 TrueTypetemplate<bool Condition, typename TrueType, typename FalseType>struct conditional {    using type = TrueType;};
// 当 Condition 为 false 时,特化版本定义 type 为 FalseTypetemplate<typename TrueType, typename FalseType>struct conditional<false, TrueType, FalseType> {    using type = FalseType;};
// 辅助模板别名 (C++14):简化 conditional 的写法template<bool Condition, typename TrueType, typename FalseType>using conditional_t = typename conditional<Condition, TrueType, FalseType>::type;
// 使用类型萃取的函数模板// 此函数分析传入类型 T 的特性,并输出相关信息template<typename T>void analyze_type(const T& value) {    std::cout << "分析类型信息:" << std::endl;    // 判断类型 T 是否为指针类型,输出结果    std::cout << "  是否是指针: " << (is_pointer<T>::value ? "是" : "否") << std::endl;    // 利用标准类型萃取判断是否为整数类型    std::cout << "  是否是整数: " << (std::is_integral<T>::value ? "是" : "否") << std::endl;    // 利用标准类型萃取判断是否为浮点数类型    std::cout << "  是否是浮点数: " << (std::is_floating_point<T>::value ? "是" : "否") << std::endl;    // 输出类型 T 的大小(字节数)    std::cout << "  类型大小: " << sizeof(T) << " 字节" << std::endl;    // 如果 T 是指针类型,则输出指针指向的类型大小    if constexpr (is_pointer<T>::value) {        std::cout << "  指向的类型大小: " << sizeof(remove_pointer_t<T>) << " 字节" << std::endl;    }    // 换行分割    std::cout << std::endl;}
int main() {    int x = 42;         // 定义一个 int 类型变量 x,并赋值 42    int* px = &x;       // 定义一个 int* 类型指针 px,指向变量 x    double y = 3.14;    // 定义一个 double 类型变量 y,并赋值 3.14analyze_type(x);    // 分析 int 类型的信息    analyze_type(px);   // 分析 int* 指针类型的信息    analyze_type(y);    // 分析 double 类型的信息// 使用条件类型选择:当 sizeof(int) >= 4 为真时,选择 int 类型;否则选择 double 类型    using int_or_double = conditional_t<sizeof(int) >= 4, int, double>;    std::cout << "条件选择的类型大小: " << sizeof(int_or_double) << " 字节" << std::endl;return 0;           // 程序正常结束,返回 0}

第七章:现代C++模板特性

7.1 概念 (Concepts) - C++20

概念允许我们对模板参数施加约束,使模板更易于使用和理解:

// 注意:这需要C++20支持// 这行注释说明本代码需要使用C++20标准,因为接下来将使用 concepts 相关的语言特性
#include <iostream>      // 引入输入输出流库,用于输出信息#include <concepts>       // 引入 C++20 的 concepts 库,用于定义概念
// 定义一个概念:可打印的类型template<typename T>concept Printable = requires(T a) {    std::cout << a;      // 要求类型 T 能够被输出到 std::cout,必须支持 << 运算符};
// 定义一个概念:有 size 方法的类型template<typename T>concept HasSize = requires(T a) {    a.size();            // 要求类型 T 必须有一个 size() 方法    { a.size() } -> std::convertible_to<std::size_t>;    // 并且 size() 方法的返回值必须可以转换为 std::size_t 类型};
// 使用概念约束的函数模板:只对 Printable 类型有效template<Printable T>void safe_print(const T& value) {    // 当传入的类型符合 Printable 概念时,可以安全地打印该值    std::cout << "安全打印: " << value << std::endl;}
// 使用概念约束的函数模板:只对具有 size() 方法的容器类型有效template<HasSize Container>void print_container_size(const Container& container) {    // 输出容器的大小    std::cout << "容器大小: " << container.size() << std::endl;}
// 更复杂的概念组合// 定义一个 Number 概念,要求类型 T 必须是整数类型或浮点类型template<typename T>concept Number = std::integral<T> || std::floating_point<T>;// 使用标准库中的 std::integral 和 std::floating_point 概念进行判断
// 定义一个平方函数 square,接受符合 Number 概念的类型参数template<Number T>T square(T value) {    // 返回传入值的平方    return value * value;}

7.2 自动类型推导的改进

现代C++在类型推导方面有很多改进:

#include <iostream>  // 引入输入输出流库,用于输出信息#include <vector>    // 引入 vector 容器,用于存储动态数组数据#include <map>       // 引入 map 容器,尽管在本示例中未被使用,通常用于存储键值对
// C++17 类模板参数推导 (CTAD) 示例int main() {    // 不需要显式指定模板参数,编译器会根据初始化列表中的元素类型推导模板参数类型    std::vector vec{1, 2, 3, 4, 5};      // 上述语句中,编译器会推导出 vec 为 std::vector<int>,因为所有元素都是 int 类型// 使用 std::pair,也可利用 CTAD 自动推导类型    std::pair p{42, 3.14};      // 这里 p 被推导为 std::pair<int, double>,因为第一个元素是 int,第二个元素是 double// 定义一个泛型 lambda 表达式,利用 auto 参数来实现模板化的函数    auto lambda = [](auto x, auto y) {        return x + y;    };    // lambda 是一个泛型函数对象,可以接受任意类型参数,只要它们支持加法运算// 调用 lambda,下面的调用将自动推导出参数类型,并根据实际参数执行相应的加法操作    std::cout << lambda(1, 2) << std::endl;       // 传入两个 int 类型的参数,结果为 int 类型相加    std::cout << lambda(1.5, 2.7) << std::endl;     // 传入两个 double 类型的参数,结果为 double 类型相加    std::cout << lambda(1, 2.5) << std::endl;       // 传入 int 与 double 类型的参数,编译器会进行类型提升,结果为 doublereturn 0;  // 程序正常结束}

总结与最佳实践

通过这个教程,我们深入了解了C++模板系统的各个方面。模板是C++中最强大的特性之一,它支持泛型编程、元编程和类型安全的代码复用。

关键要点回顾:

  1. 模板基础: 模板允许编写与类型无关的代码,编译器根据使用情况自动生成具体的代码实例。

  2. 函数模板: 用于创建泛型函数,支持类型推导和显式类型指定。

  3. 类模板: 用于创建泛型类,可以有多个模板参数,包括类型参数和非类型参数。

  4. 模板特化

  • 全特化:为特定类型提供完全不同的实现

  • 偏特化:为模板参数的某些组合提供特殊实现(仅限类模板)

  • 高级技术

    • SFINAE:利用替换失败进行条件编译

    • 变参模板:处理可变数量的参数

    • 模板元编程:在编译期进行计算和类型操作

  • 现代特性

    • 概念 (C++20):为模板参数添加约束

    • 改进的类型推导:CTAD、auto、泛型lambda

    使用建议:

    • 优先使用标准库提供的模板和算法

    • 编写模板时要考虑错误信息的可读性

    • 合理使用特化,不要过度复杂化

    • 在现代C++中,优先使用constexpr而不是模板元编程进行编译期计算

    • 使用概念 (如果有C++20支持) 来使模板更易于理解和使用

    模板是C++的核心特性,掌握它们将大大提升您的C++编程能力。记住,模板的学习是一个渐进的过程,从简单的泛型函数开始,逐步深入到更高级的技术。


http://www.hkcw.cn/article/dpQYVSagDy.shtml

相关文章

VBA数据库解决方案二十:Select表达式From区域Where条件Order by

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

【2025最新】Java图书借阅管理系统:从课程作业到实战应用的完整解决方案

【2025最新】Java图书借阅管理系统&#xff1a;从课程作业到实战应用的完整解决方案 目录 【2025最新】Java图书借阅管理系统&#xff1a;从课程作业到实战应用的完整解决方案**系统概述** **核心功能模块详解****1. 系统登录与权限控制****2. 借阅管理模块****3. 用户角色管理…

结合源码分析Redis的内存回收和内存淘汰机制,LRU和LFU是如何进行计算的?

Redis 内存回收 1. 过期 key 处理 Redis 之所以性能强&#xff0c;最主要的原因就是基于内存存储。然而单节点的 Redis 其内存大小不宜过大&#xff0c;会影响持久化或主从同步性能。我们可以通过修改配置文件来设置Redis的最大内存&#xff1a; 当内存使用达到上限时&#…

NLP学习路线图(十五):TF-IDF(词频-逆文档频率)

在自然语言处理&#xff08;NLP&#xff09;的浩瀚宇宙中&#xff0c;TF-IDF&#xff08;词频-逆文档频率&#xff09; 犹如一颗恒星&#xff0c;虽古老却依然璀璨。当ChatGPT、BERT等大模型光芒四射时&#xff0c;TF-IDF作为传统方法的代表&#xff0c;其简洁性、高效性与可解…

C++11(上)

历史&#xff1a; 在C98版本后&#xff0c;C11是一次大版本的更新。在C11中新增了许多有用的东西。接下来将由小编来带领大家介绍C11中新增的内容。 列表初始化: 在C中&#xff0c;列表初始化&#xff08;也称为统一初始化或花括号初始化&#xff09;是一种使用花括号 {} 来初…

从TCO角度分析IBM Cognos Analytics

一、总拥有成本&#xff08;TCO&#xff09;分析 像 Cognos Analytics 这样成熟的企业级 BI 平台&#xff0c;在与新兴的敏捷 BI 工具竞争中&#xff0c;依然能够保持其独特价值和竞争力的关键所在&#xff0c;尤其从企业和组织的长远发展、团队协作以及总拥有成本&#xff08…

使用西门子博图V16时遇到了搜索功能报错的问题,提示缺少SIMATIC Visualization Architect组件怎么办,全网首发

先上解决方案&#xff0c;这个太简单了&#xff0c;直接上官网下载&#xff0c;这个安装包40M&#xff0c;很快就下载完了&#xff0c;然后直接安装就可以了。 官网链接SIMATIC Visualization Architect V16 TRIAL Download - ID: 109772966 - Industry Support Siemens 今天我…

STM32G4 电机外设篇(三) TIM1 发波 和 ADC COMP DAC级联

目录 一、STM32G4 电机外设篇&#xff08;三&#xff09; TIM1 发波 和 ADC COMP DAC级联1 TIM1 高级定时器发波1.1 stm32cubemx配置 2 TIM1 ADC COMP DAC级联2.1 stm32cubemx配置 附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^) 一、STM32G4 电机外设篇&#xff08;三&…

12 Java GUI

Java 在图形开发中的占比并不是特别突出&#xff0c;尤其在传统的客户端图形界面开发方面。不是现代 UI 设计的首选 C#的WinForms&#xff08;传统&#xff09;、WPF&#xff08;现代&#xff09;是Windows 桌面开发的王者 跨平台&#xff08;Windows/macOS/Linux&#xff09;&…

当AI遇见千年古韵:解密“古韵智绘”,让传统纹样焕发新生机

目录: 引言:当千年古韵遇上AI,一场跨越时空的对话“古韵智绘”:不止于复刻,更是创新的引擎核心技术揭秘:AI如何“理解”并“创作”传统纹样? 基石:海量纹样数据库与智能特征提取神笔:基于GANs的AI纹样生成器魔术:风格迁移与融合的艺术桥梁:交互式编辑与开放API接口系…

[AD] Reaper NBNS+LLMNR+Logon 4624+Logon ID

QA QAForela-Wkstn001 的 IP 位址是什麼&#xff1f;172.17.79.129Forela-Wkstn002 的 IP 位址是什麼&#xff1f;172.17.79.136被攻擊者竊取雜湊值的帳戶的使用者名稱是什麼&#xff1f;arthur.kyle攻擊者用來攔截憑證的未知設備的 IP 位址是什麼&#xff1f;172.17.79.135受…

RAG入门之数据导入

LangChain 是什么 LangChain 是一个用于构建基于大语言模型&#xff08;LLM&#xff09;应用的开源框架。它提供了一套工具和抽象&#xff0c;让开发者能够轻松构建复杂的AI应用。 LangChain 的核心功能 文档加载和处理&#xff1a;支持多种格式&#xff08;PDF、文本、网页…

科研学习|科研软件——激活后的Origin导出图时突然出现了demo水印

问题&#xff1a;画完图在导出图形时&#xff0c;导出的图有demo水印&#xff0c;如下图。 解决方法1&#xff1a;右击选择以管理员身份运行。 解决方法2&#xff1a;找到该软件的保存路径&#xff0c;双击Origin64.exe

一:UML类图

类之间的关系 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 学习设计模式的第一步是看懂UML类图&#xff0c;类图能直观的表达类、对象之间的关系&#xff0c;这将有助于后续对代码的编写。 常见的类之间的关系包括&#xff1a;继承…

Python数学可视化——环境搭建与基础绘图

Python数学可视化——环境搭建与基础绘图 数学函数可视化入门&#xff08;一次函数/三角函数&#xff09; 本节将建立Python科学计算环境&#xff0c;并创建基础函数绘图工具&#xff0c;可生成一次函数和三角函数的可视化图像&#xff0c;同时结合物理中的匀速直线运动案例。…

mask2former训练自己的语义分割数据集

一、环境配置 1.1下载源码 mask2former: https://github.com/facebookresearch/Mask2Former/tree/maindetectron2: https://github.com/facebookresearch/detectron2下载完后&#xff0c;新建一个文件夹&#xff0c;起个名字&#xff08;我起的Mask2Former-main&#xff09…

如何使用1panel部署linux网站

找到官网&#xff0c;尝试一下在线安装 如果在线不成功&#xff0c;试一下离线安装 按照指令一步步执行即可&#xff0c;注意换成新版本的名称即可 如果成功&#xff0c;你会看到这个页面 1Panel Log]: [1Panel Log]: 感谢您的耐心等待&#xff0c;安装已完成 [1Panel Log]:…

个人用户进行LLMs本地部署前如何自查和筛选

一、个人用户硬件自查清单&#xff08;从核心到次要&#xff09; 1. 显卡&#xff08;GPU&#xff09;——决定性因素 显存容量&#xff08;关键指标&#xff09;&#xff1a; 入门级&#xff08;8~12GB&#xff09;&#xff1a;可运行7B模型&#xff08;4bit量化&#xff09;…

java Map双列集合

单列集合&#xff1a;一次只能添加一个元素 双列集合&#xff1a;一次添加两个元素&#xff0c;左边的叫键&#xff08;唯一的不能重复&#xff09;&#xff0c;右边叫值&#xff08;可以重复&#xff09;&#xff0c;键和值一一对应。这样一对叫&#xff1a;键值对/键值对对象…

在IIS上无法使用PUT等请求

错误来源&#xff1a; chat:1 Access to XMLHttpRequest at http://101.126.139.3:11000/api/receiver/message from origin http://101.126.139.3 has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource. 其实我的后…