首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C++模板与泛型编程】模板编译模型

【C++模板与泛型编程】模板编译模型

作者头像
用户12001910
发布2026-01-21 17:30:36
发布2026-01-21 17:30:36
110
举报

在 C++ 编程中,模板是实现泛型编程的核心机制,它允许我们编写与类型无关的代码,极大地提高了代码的复用性。然而,模板的编译过程与普通代码有很大不同,理解其编译模型对于高效使用模板至关重要。

一、模板编译的基本原理

1.1 传统编译流程与模板编译的区别

传统 C++ 代码的编译流程分为三个主要阶段:

  1. 预处理:处理头文件包含、宏替换等
  2. 编译:将源代码转换为目标文件(.obj 或 .o)
  3. 链接:将多个目标文件合并为可执行文件

而模板代码的编译流程更为复杂,主要区别在于实例化阶段:

  1. 预处理:与传统流程相同
  2. 编译模板定义:编译器解析模板代码,但不生成实际代码
  3. 实例化:当模板被使用时,编译器根据具体类型生成对应的实例代码
  4. 链接:处理实例化代码的引用关系

1.2 模板编译的延迟特性

模板编译的一个重要特性是延迟编译(Lazy Compilation):

  • 模板定义本身不会生成代码:编译器仅验证模板语法
  • 只有在实例化时才生成具体代码:根据使用的具体类型生成对应的函数或类

这种延迟特性导致模板代码的编译错误可能在实例化时才被发现,而非模板定义时。

二、单一定义规则(ODR)与模板

2.1 ODR 规则概述

单一定义规则(One Definition Rule)是 C++ 的核心规则之一,它规定:

  1. 每个非内联函数或变量在整个程序中只能有一个定义
  2. 每个类、结构体、枚举类型在每个翻译单元中只能有一个定义
  3. 模板、内联函数和内联变量可以在多个翻译单元中重复定义,但必须完全相同

2.2 模板与 ODR 的关系

模板看似违反 ODR,因为它们可以在多个头文件中重复定义。但实际上:

  • 模板定义本身不是定义:它只是一个 “蓝图”
  • 模板实例化才产生定义:每个实例化在程序中只能有一个定义

例如,以下代码在多个文件中包含同一个模板定义是合法的:

代码语言:javascript
复制
// my_template.h
template <typename T>
T add(T a, T b) {
    return a + b;
}

// file1.cpp
#include "my_template.h"
void f1() { add(1, 2); }  // 实例化 add<int>

// file2.cpp
#include "my_template.h"
void f2() { add(3, 4); }  // 再次实例化 add<int>

虽然 add 模板在两个文件中都被实例化为 add<int>,但链接器会正确处理重复实例化,确保最终程序中只有一个定义。

三、包含编译模型(Inclusion Model)

3.1 基本概念与实现方式

包含编译模型是 C++ 模板最常见的编译模型,其核心思想是:

将模板的定义和声明都放在头文件中,通过包含头文件来实现实例化

例如: 

代码语言:javascript
复制
// my_vector.h
#ifndef MY_VECTOR_H
#define MY_VECTOR_H

template <typename T>
class MyVector {
private:
    T* data;
    size_t size;
public:
    MyVector();
    ~MyVector();
    void push_back(const T& value);
    T& operator[](size_t index);
};

// 所有成员函数的定义都放在头文件中
template <typename T>
MyVector<T>::MyVector() : data(nullptr), size(0) {}

template <typename T>
MyVector<T>::~MyVector() { delete[] data; }

template <typename T>
void MyVector<T>::push_back(const T& value) {
    // ... 实现略 ...
}

template <typename T>
T& MyVector<T>::operator[](size_t index) {
    return data[index];
}

#endif // MY_VECTOR_H

3.2 工作原理

当某个源文件包含这个头文件并使用 MyVector 时:

  1. 编译器看到模板定义
  2. 当遇到具体的实例化(如 MyVector<int>)时,编译器使用模板定义生成对应的代码
  3. 生成的实例代码成为该翻译单元的一部分

3.3 优缺点分析

优点

  1. 简单易用:无需额外的编译步骤
  2. 可靠性高:编译器能看到完整的模板定义,减少链接错误
  3. 兼容性好:所有编译器都支持这种模型

缺点

  1. 编译时间增加:每个包含模板头文件的源文件都会编译模板代码
  2. 代码膨胀:相同的模板实例可能在多个目标文件中重复生成
  3. 维护困难:大型模板定义会使头文件变得庞大

3.4 示例代码

下面是一个简单的包含编译模型示例: 

代码语言:javascript
复制
// stack.h
#ifndef STACK_H
#define STACK_H

#include <stdexcept>

template <typename T>
class Stack {
private:
    T* data;
    size_t size;
    size_t capacity;

public:
    Stack();
    ~Stack();
    void push(const T& value);
    void pop();
    T& top();
    const T& top() const;
    bool empty() const;
};

// 所有成员函数的定义都在头文件中
template <typename T>
Stack<T>::Stack() : data(nullptr), size(0), capacity(0) {}

template <typename T>
Stack<T>::~Stack() {
    delete[] data;
}

template <typename T>
void Stack<T>::push(const T& value) {
    if (size >= capacity) {
        capacity = capacity == 0 ? 1 : capacity * 2;
        T* newData = new T[capacity];
        for (size_t i = 0; i < size; ++i) {
            newData[i] = data[i];
        }
        delete[] data;
        data = newData;
    }
    data[size++] = value;
}

template <typename T>
void Stack<T>::pop() {
    if (empty()) {
        throw std::underflow_error("Stack is empty");
    }
    --size;
}

template <typename T>
T& Stack<T>::top() {
    if (empty()) {
        throw std::underflow_error("Stack is empty");
    }
    return data[size - 1];
}

template <typename T>
const T& Stack<T>::top() const {
    if (empty()) {
        throw std::underflow_error("Stack is empty");
    }
    return data[size - 1];
}

template <typename T>
bool Stack<T>::empty() const {
    return size == 0;
}

#endif // STACK_H

使用这个栈模板的代码可以简单地包含头文件:

代码语言:javascript
复制
// main.cpp
#include <iostream>
#include "stack.h"

int main() {
    Stack<int> intStack;
    intStack.push(10);
    intStack.push(20);
    std::cout << "Top: " << intStack.top() << std::endl;
    intStack.pop();
    std::cout << "Top after pop: " << intStack.top() << std::endl;
    return 0;
}

四、分别编译模型(Separate Compilation Model)

4.1 基本概念与实现方式

分别编译模型试图将模板的声明和定义分开,类似于普通类的实现方式:

  1. 头文件:包含模板的声明
  2. 源文件:包含模板的定义

例如:

代码语言:javascript
复制
// my_vector.h (声明)
#ifndef MY_VECTOR_H
#define MY_VECTOR_H

template <typename T>
class MyVector {
private:
    T* data;
    size_t size;
public:
    MyVector();
    ~MyVector();
    void push_back(const T& value);
    T& operator[](size_t index);
};

#endif // MY_VECTOR_H

// my_vector.cpp (定义)
#include "my_vector.h"

template <typename T>
MyVector<T>::MyVector() : data(nullptr), size(0) {}

template <typename T>
MyVector<T>::~MyVector() { delete[] data; }

template <typename T>
void MyVector<T>::push_back(const T& value) {
    // ... 实现略 ...
}

template <typename T>
T& MyVector<T>::operator[](size_t index) {
    return data[index];
}

4.2 传统分别编译模型的问题

这种方法在普通类中工作良好,但对于模板会导致链接错误: 

代码语言:javascript
复制
// main.cpp
#include "my_vector.h"

int main() {
    MyVector<int> vec;  // 使用 MyVector<int>
    vec.push_back(42);  // 链接错误:找不到 MyVector<int>::push_back 的定义
    return 0;
}

问题原因

  1. 编译器编译 my_vector.cpp 时,没有看到任何实例化请求,因此不会生成任何实例代码
  2. 编译器编译 main.cpp 时,只看到模板声明,没有看到定义,无法生成实例代码
  3. 链接阶段,main.cpp 引用的 MyVector<int> 成员函数找不到定义

4.3 显式实例化(Explicit Instantiation)

为了解决分别编译模型的问题,可以使用显式实例化: 

代码语言:javascript
复制
// my_vector.cpp (定义)
#include "my_vector.h"

template <typename T>
MyVector<T>::MyVector() : data(nullptr), size(0) {}

// 其他成员函数定义...

// 显式实例化特定类型
template class MyVector<int>;      // 实例化 MyVector<int>
template class MyVector<double>;   // 实例化 MyVector<double>

这样,my_vector.cpp 会生成 MyVector<int> 和 MyVector<double> 的实例代码,其他文件可以直接使用这些实例。

4.4 优缺点分析

优点

  1. 减少编译时间:模板定义只编译一次
  2. 减小头文件体积:头文件只包含声明,不包含实现
  3. 保护实现细节:可以隐藏模板的实现

缺点

  1. 需要预先知道所有实例化类型:必须在源文件中显式实例化所有需要的类型
  2. 缺乏灵活性:新增实例化类型需要修改源文件并重新编译
  3. 可能导致链接错误:如果使用了未显式实例化的类型,会导致链接错误

4.5 示例代码

下面是一个使用分别编译模型和显式实例化的示例: 

代码语言:javascript
复制
// calculator.h (声明)
#ifndef CALCULATOR_H
#define CALCULATOR_H

template <typename T>
class Calculator {
public:
    T add(T a, T b);
    T subtract(T a, T b);
    T multiply(T a, T b);
    T divide(T a, T b);
};

#endif // CALCULATOR_H

// calculator.cpp (定义)
#include "calculator.h"
#include <stdexcept>

template <typename T>
T Calculator<T>::add(T a, T b) {
    return a + b;
}

template <typename T>
T Calculator<T>::subtract(T a, T b) {
    return a - b;
}

template <typename T>
T Calculator<T>::multiply(T a, T b) {
    return a * b;
}

template <typename T>
T Calculator<T>::divide(T a, T b) {
    if (b == 0) {
        throw std::runtime_error("Division by zero");
    }
    return a / b;
}

// 显式实例化
template class Calculator<int>;
template class Calculator<double>;

使用这个计算器模板的代码:

代码语言:javascript
复制
// main.cpp
#include <iostream>
#include "calculator.h"

int main() {
    Calculator<int> intCalc;
    std::cout << "5 + 3 = " << intCalc.add(5, 3) << std::endl;
    
    Calculator<double> doubleCalc;
    std::cout << "5.5 / 2.2 = " << doubleCalc.divide(5.5, 2.2) << std::endl;
    
    // 以下行会导致链接错误,因为没有显式实例化 Calculator<std::string>
    // Calculator<std::string> stringCalc;
    
    return 0;
}

五、显式实例化声明(Extern Template)

5.1 基本概念与语法

C++11 引入了显式实例化声明(Extern Template),允许我们告诉编译器:

某个模板实例的定义在其他翻译单元中,不要在当前单元中生成它

语法如下:

代码语言:javascript
复制
extern template declaration;

例如:

代码语言:javascript
复制
extern template class std::vector<int>;  // 声明 std::vector<int> 的实例化在其他地方

5.2 工作原理

当编译器遇到 extern template 声明时:

  1. 不会在当前翻译单元中生成对应的实例代码
  2. 假设该实例在其他翻译单元中被显式实例化
  3. 链接时会从其他单元查找该实例的定义

5.3 使用场景

主要用于减少编译时间避免代码膨胀,特别是在大型项目中:

  1. 在头文件中使用 extern template 声明常用实例
  2. 在一个源文件中显式实例化这些模板

例如: 

代码语言:javascript
复制
// my_list.h
#ifndef MY_LIST_H
#define MY_LIST_H

template <typename T>
class MyList {
    // ... 类定义 ...
};

// 声明常用实例在其他地方实例化
extern template class MyList<int>;
extern template class MyList<double>;

#endif // MY_LIST_H

// my_list.cpp
#include "my_list.h"

// 显式实例化常用类型
template class MyList<int>;
template class MyList<double>;

5.4 示例代码

下面是一个使用显式实例化声明的示例: 

代码语言:javascript
复制
// matrix.h
#ifndef MATRIX_H
#define MATRIX_H

#include <vector>

template <typename T>
class Matrix {
private:
    std::vector<std::vector<T>> data;
    size_t rows, cols;

public:
    Matrix(size_t r, size_t c);
    T& operator()(size_t i, size_t j);
    const T& operator()(size_t i, size_t j) const;
    Matrix operator+(const Matrix& other) const;
};

// 声明常用实例
extern template class Matrix<int>;
extern template class Matrix<double>;

#endif // MATRIX_H

// matrix.cpp
#include "matrix.h"

template <typename T>
Matrix<T>::Matrix(size_t r, size_t c) : rows(r), cols(c) {
    data.resize(r, std::vector<T>(c));
}

template <typename T>
T& Matrix<T>::operator()(size_t i, size_t j) {
    return data[i][j];
}

template <typename T>
const T& Matrix<T>::operator()(size_t i, size_t j) const {
    return data[i][j];
}

template <typename T>
Matrix<T> Matrix<T>::operator+(const Matrix& other) const {
    if (rows != other.rows || cols != other.cols) {
        throw std::invalid_argument("Matrix dimensions must match");
    }
    Matrix result(rows, cols);
    for (size_t i = 0; i < rows; ++i) {
        for (size_t j = 0; j < cols; ++j) {
            result(i, j) = (*this)(i, j) + other(i, j);
        }
    }
    return result;
}

// 显式实例化常用类型
template class Matrix<int>;
template class Matrix<double>;

使用这个矩阵模板的代码:

代码语言:javascript
复制
// main.cpp
#include <iostream>
#include "matrix.h"

int main() {
    Matrix<int> mat1(2, 2);
    mat1(0, 0) = 1; mat1(0, 1) = 2;
    mat1(1, 0) = 3; mat1(1, 1) = 4;

    Matrix<int> mat2(2, 2);
    mat2(0, 0) = 5; mat2(0, 1) = 6;
    mat2(1, 0) = 7; mat2(1, 1) = 8;

    auto sum = mat1 + mat2;
    std::cout << "Sum: " << sum(0, 0) << " " << sum(0, 1) << "\n"
              << "     " << sum(1, 0) << " " << sum(1, 1) << std::endl;

    return 0;
}

六、编译模型的实现细节

6.1 实例化点(Point of Instantiation)

模板实例化发生在实例化点,这是编译器确定的一个位置:

  • 对于函数模板,实例化点通常在调用该模板函数的代码之后
  • 对于类模板,实例化点通常在使用该类模板的完整类型之后
代码语言:javascript
复制
// ---------- example.h ----------
template<typename T>
class Container {
public:
    void add(const T& value);
};

// ---------- example.cpp ----------
#include "example.h"

template<typename T>
void Container<T>::add(const T& value) {
    // 实现细节
}

// 显式实例化
template class Container<int>;

// ---------- main.cpp ----------
#include "example.h"

int main() {
    Container<int> c;  // 实例化点在此之后
    c.add(42);         // 调用已实例化的成员函数
    return 0;
}

6.2 实例化策略

编译器在实例化模板时有两种主要策略:

  1. 隐式实例化(Implicit Instantiation):当编译器遇到模板使用时,自动生成对应的实例。这是最常见的实例化方式。
  2. 显式实例化(Explicit Instantiation):通过template关键字显式指定要实例化的模板类型。

6.3 实例化依赖

模板实例化可能依赖于其他类型或表达式: 

代码语言:javascript
复制
template<typename T>
struct Identity {
    using type = T;
};

template<typename T>
void print_type(typename Identity<T>::type value) {
    // 函数实现
}

int main() {
    print_type<int>(42);  // 实例化print_type<int>
    return 0;
}

print_type的参数类型是typename Identity<T>::type,这是一个依赖名称(Dependent Name)。编译器在实例化时需要解析这个依赖名称。

七、编译模型相关的问题与解决方案

7.1 模板定义不可见问题

最常见的错误是在模板实例化点无法访问模板的完整定义: 

代码语言:javascript
复制
// ---------- error_example.h ----------
template<typename T>
class Calculator {
public:
    T add(T a, T b);
};

// ---------- error_example.cpp ----------
#include "error_example.h"

template<typename T>
T Calculator<T>::add(T a, T b) {
    return a + b;
}

// ---------- main.cpp ----------
#include "error_example.h"

int main() {
    Calculator<int> calc;
    int result = calc.add(3, 4);  // 错误:无法找到add的定义
    return 0;
}

解决方案

  • 将模板实现放在头文件中
  • 使用显式实例化
  • 使用 export 关键字(C++11 已弃用)

7.2 重复实例化问题

如果多个源文件包含相同的模板定义并进行实例化,会导致重复实例化,增加编译时间和可执行文件大小。

解决方案

  • 使用显式实例化减少重复实例化
  • 使用外部模板避免不必要的实例化

7.3 编译时间过长

大型项目中,模板的广泛使用可能导致编译时间显著增加。

解决方案

  • 使用预编译头文件(PCH)
  • 合理使用显式实例化和外部模板
  • 减少模板的复杂性,避免深层模板嵌套

八、高级编译模型技术

8.1 模板特化与编译模型

模板特化允许我们为特定类型提供定制的实现: 

代码语言:javascript
复制
// 通用模板
template<typename T>
struct IsPointer {
    static constexpr bool value = false;
};

// 指针特化
template<typename T>
struct IsPointer<T*> {
    static constexpr bool value = true;
};

模板特化的编译规则与普通模板略有不同,需要确保特化定义在使用之前可见。

8.2 分离编译模型的替代方案

除了包含模型和显式实例化,还有一些替代方案可以实现模板的分离编译:

  1. PImpl 惯用法(Pointer to Implementation):将模板类的实现细节封装在一个单独的实现类中,通过指针访问。
  2. 工厂模式:使用工厂函数创建模板类的实例,将实例化逻辑集中在一个地方。
  3. 编译防火墙(Compilation Firewall):通过接口类和抽象基类隔离模板实现,减少编译依赖。

九、代码示例:完整的模板编译模型演示

下面是一个完整的示例,展示了不同编译模型的实现方式: 

代码语言:javascript
复制
// ---------- vector.h ----------
#ifndef VECTOR_H
#define VECTOR_H

#include <cstddef>
#include <stdexcept>

template<typename T>
class Vector {
private:
    T* data;
    size_t size_;
    size_t capacity;

public:
    // 构造函数
    explicit Vector(size_t initial_size = 0);
    
    // 析构函数
    ~Vector();
    
    // 拷贝构造函数
    Vector(const Vector& other);
    
    // 赋值运算符
    Vector& operator=(const Vector& other);
    
    // 访问元素
    T& operator[](size_t index);
    const T& operator[](size_t index) const;
    
    // 获取大小和容量
    size_t size() const { return size_; }
    bool empty() const { return size_ == 0; }
    
    // 添加元素
    void push_back(const T& value);
    
    // 删除元素
    void pop_back();
};

// 包含模型:将实现放在头文件中(方法1)
#include "vector_impl.h"

#endif // VECTOR_H

// ---------- vector_impl.h ----------
#ifndef VECTOR_IMPL_H
#define VECTOR_IMPL_H

template<typename T>
Vector<T>::Vector(size_t initial_size)
    : data(new T[initial_size]), size_(initial_size), capacity(initial_size) {}

template<typename T>
Vector<T>::~Vector() {
    delete[] data;
}

// 其他成员函数的实现...

#endif // VECTOR_IMPL_H

// ---------- main.cpp ----------
#include "vector.h"
#include <iostream>

int main() {
    Vector<int> vec;
    vec.push_back(10);
    vec.push_back(20);
    
    for (size_t i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

如果想使用显式实例化模型,可以这样修改:

代码语言:javascript
复制
// ---------- vector.h ----------
#ifndef VECTOR_H
#define VECTOR_H

#include <cstddef>
#include <stdexcept>

template<typename T>
class Vector {
    // 类定义保持不变...
};

// 声明成员函数
template<typename T>
Vector<T>::Vector(size_t initial_size);

template<typename T>
Vector<T>::~Vector();

// 其他成员函数声明...

#endif // VECTOR_H

// ---------- vector.cpp ----------
#include "vector.h"

// 实现成员函数
template<typename T>
Vector<T>::Vector(size_t initial_size)
    : data(new T[initial_size]), size_(initial_size), capacity(initial_size) {}

// 其他成员函数的实现...

// 显式实例化
template class Vector<int>;
template class Vector<double>;

// ---------- main.cpp ----------
#include "vector.h"
#include <iostream>

int main() {
    Vector<int> vec;  // 使用显式实例化的Vector<int>
    // ...
}

十、编译模型的选择与最佳实践

10.1 选择合适的编译模型

根据项目特点和需求,选择合适的编译模型:

  • 小型项目或快速原型:使用包含编译模型,简单直接
  • 大型项目或性能敏感项目:使用分别编译模型 + 显式实例化,减少编译时间
  • 模板库开发:使用包含编译模型,提供最大灵活性

10.2 最佳实践

  • 优先使用包含编译模型:除非有明确的性能需求,否则应优先使用包含编译模型
  • 使用显式实例化优化性能:对于常用类型,在单独的源文件中显式实例化
  • 使用 extern template 减少重复实例化:在头文件中声明常用实例,避免多个文件重复实例化
  • 避免在头文件中包含不必要的实现:保持头文件简洁,只包含必要的定义
  • 使用 pragma once 或头文件保护:防止头文件被重复包含
  • 使用 C++20 模块:模块提供了更好的编译模型,避免了头文件的许多问题

10.3 编译模型与重载操作符、类型转换的结合

当模板与重载操作符、类型转换结合时,编译模型的选择尤为重要:

  • 操作符重载:确保操作符重载的定义在实例化点可见
  • 类型转换:类型转换函数的定义也需要在实例化点可见
  • 模板特化:特化版本的模板需要在实例化前定义

十一、C++20 模块与模板编译

11.1 模块概述

C++20 引入的模块(Modules)是一种新的代码组织和编译机制,旨在替代传统的头文件:

  • 模块化代码:将代码分为独立的模块单元
  • 编译优化:模块只编译一次,后续使用无需重新编译
  • 接口与实现分离:明确区分模块的接口和实现部分

11.2 模块与模板编译的关系

模块为模板编译提供了更好的解决方案:

  • 避免重复编译:模板定义在模块中只编译一次
  • 减少依赖问题:模块依赖关系明确,避免了头文件包含的许多问题
  • 更快的编译速度:模块的增量编译效率更高

11.3 模块语法示例

下面是一个使用模块的模板示例: 

代码语言:javascript
复制
// my_vector.module.cpp (模块接口)
export module my_vector;

export template <typename T>
class MyVector {
private:
    T* data;
    size_t size;
public:
    MyVector();
    ~MyVector();
    void push_back(const T& value);
    T& operator[](size_t index);
    size_t getSize() const;
};

// my_vector_impl.module.cpp (模块实现)
module my_vector;

template <typename T>
MyVector<T>::MyVector() : data(nullptr), size(0) {}

template <typename T>
MyVector<T>::~MyVector() { delete[] data; }

template <typename T>
void MyVector<T>::push_back(const T& value) {
    // ... 实现略 ...
}

template <typename T>
T& MyVector<T>::operator[](size_t index) {
    return data[index];
}

template <typename T>
size_t MyVector<T>::getSize() const {
    return size;
}

// 显式实例化常用类型
template class MyVector<int>;
template class MyVector<double>;

使用模块的代码:

代码语言:javascript
复制
// main.cpp
import my_vector;

int main() {
    MyVector<int> vec;
    vec.push_back(42);
    std::cout << "Vector size: " << vec.getSize() << std::endl;
    return 0;
}

十二、总结

C++ 模板的编译模型是一个复杂但重要的主题,理解不同的编译模型对于编写高效、可维护的模板代码至关重要。本文详细介绍三种主要的编译模型:

  • 包含编译模型:将模板定义放在头文件中,简单易用,但可能导致编译时间增加和代码膨胀
  • 分别编译模型:将模板声明和定义分开,需要显式实例化,适合大型项目
  • 显式实例化声明:使用 extern template 减少重复实例化,优化编译性能

此外,还学习了 C++20 模块为模板编译带来的改进。在实际编程中,应根据项目需求选择合适的编译模型,并遵循最佳实践,以确保代码既高效又易于维护。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、模板编译的基本原理
    • 1.1 传统编译流程与模板编译的区别
    • 1.2 模板编译的延迟特性
  • 二、单一定义规则(ODR)与模板
    • 2.1 ODR 规则概述
    • 2.2 模板与 ODR 的关系
  • 三、包含编译模型(Inclusion Model)
    • 3.1 基本概念与实现方式
    • 3.2 工作原理
    • 3.3 优缺点分析
    • 3.4 示例代码
  • 四、分别编译模型(Separate Compilation Model)
    • 4.1 基本概念与实现方式
    • 4.2 传统分别编译模型的问题
    • 4.3 显式实例化(Explicit Instantiation)
    • 4.4 优缺点分析
    • 4.5 示例代码
  • 五、显式实例化声明(Extern Template)
    • 5.1 基本概念与语法
    • 5.2 工作原理
    • 5.3 使用场景
    • 5.4 示例代码
  • 六、编译模型的实现细节
    • 6.1 实例化点(Point of Instantiation)
    • 6.2 实例化策略
    • 6.3 实例化依赖
  • 七、编译模型相关的问题与解决方案
    • 7.1 模板定义不可见问题
    • 7.2 重复实例化问题
    • 7.3 编译时间过长
  • 八、高级编译模型技术
    • 8.1 模板特化与编译模型
    • 8.2 分离编译模型的替代方案
  • 九、代码示例:完整的模板编译模型演示
  • 十、编译模型的选择与最佳实践
    • 10.1 选择合适的编译模型
    • 10.2 最佳实践
    • 10.3 编译模型与重载操作符、类型转换的结合
  • 十一、C++20 模块与模板编译
    • 11.1 模块概述
    • 11.2 模块与模板编译的关系
    • 11.3 模块语法示例
  • 十二、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档