第19章 模板和泛型编程

模板的底层实现原理

模板实例化机制

模板是C++中实现泛型编程的核心机制,其底层实现涉及编译期实例化过程:

  1. 模板定义:编译器解析模板代码,但不生成具体代码
  2. 实例化触发:当模板被使用时(如创建对象或调用函数)
  3. 类型替换:编译器将模板参数替换为具体类型
  4. 代码生成:为每个唯一的模板实例生成专门的代码
  5. 优化:对生成的代码应用常规编译优化

模板实例化的编译器处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 模板定义
template <typename T>
T maximum(T a, T b) {
return a > b ? a : b;
}

// 实例化触发
int main() {
int a = 1, b = 2;
double c = 1.5, d = 2.5;

// 触发 int 版本的实例化
int max_int = maximum(a, b);

// 触发 double 版本的实例化
double max_double = maximum(c, d);

return 0;
}

编译器会为 maximum<int>maximum<double> 生成两个不同的函数实例,每个实例都是针对具体类型优化的代码。

模板的名称查找规则

  1. 依赖名称查找(ADL):模板中的依赖名称会在实例化时查找
  2. 两阶段查找
    • 第一阶段:解析模板时,查找非依赖名称
    • 第二阶段:实例化时,查找依赖名称
  3. 模板参数推导:编译器根据实参类型推导模板参数类型

高级模板元编程

编译期计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 编译期计算阶乘
template <unsigned int N>
struct Factorial {
static constexpr unsigned int value = N * Factorial<N - 1>::value;
};

// 特化作为终止条件
template <>
struct Factorial<0> {
static constexpr unsigned int value = 1;
};

// 使用编译期计算
constexpr unsigned int fact_5 = Factorial<5>::value; // 编译期计算为 120

类型 traits

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 自定义类型 trait
template <typename T>
struct is_integral {
static constexpr bool value = false;
};

// 特化基本整型
template <>
struct is_integral<int> {
static constexpr bool value = true;
};

template <>
struct is_integral<long> {
static constexpr bool value = true;
};

// 使用类型 trait
template <typename T>
void process(T value) {
if constexpr (is_integral<T>::value) {
std::cout << "Processing integral type: " << value << std::endl;
} else {
std::cout << "Processing non-integral type" << std::endl;
}
}

SFINAE 技术

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// SFINAE: Substitution Failure Is Not An Error
template <typename T>
auto test(T t) -> decltype(t.foo(), std::true_type()) {
return std::true_type{};
}

template <typename T>
std::false_type test(...);

template <typename T>
struct has_foo : decltype(test<T>(std::declval<T>())) {
};

// 使用 SFINAE
template <typename T>
void process_if_has_foo(T t) {
if constexpr (has_foo<T>::value) {
t.foo();
} else {
std::cout << "Type does not have foo() method" << std::endl;
}
}

模板元编程的实际应用

编译期反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <vector>

// 编译期类型信息获取
template <typename T>
struct TypeInfo {
static constexpr const char* name() {
return "unknown";
}
};

// 特化基本类型
template <>
struct TypeInfo<int> {
static constexpr const char* name() {
return "int";
}
};

template <>
struct TypeInfo<double> {
static constexpr const char* name() {
return "double";
}
};

template <typename T>
struct TypeInfo<std::vector<T>> {
static constexpr const char* name() {
return "std::vector<>";
}
};

// 使用类型信息
template <typename T>
void printTypeInfo() {
std::cout << "Type: " << TypeInfo<T>::name() << std::endl;
}

constexpr 函数的高级应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 编译期字符串长度计算
constexpr size_t strlen_constexpr(const char* str) {
return *str ? 1 + strlen_constexpr(str + 1) : 0;
}

// 编译期字符串比较
constexpr bool strcmp_constexpr(const char* a, const char* b) {
return *a == *b && (*a == '\0' || strcmp_constexpr(a + 1, b + 1));
}

// 编译期字符串哈希
constexpr size_t hash_string(const char* str, size_t seed = 0) {
return *str ? hash_string(str + 1, seed * 31 + *str) : seed;
}

可变参数模板

可变参数模板是C++11引入的重要特性,它允许模板接受任意数量的模板参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 可变参数模板的基本形式
template <typename... Args>
void print(Args... args) {
// 处理可变参数
}

// 递归展开可变参数
template <typename T>
void print_single(T t) {
std::cout << t << std::endl;
}

template <typename T, typename... Args>
void print(T first, Args... rest) {
print_single(first);
print(rest...); // 递归调用
}

// 使用折叠表达式(C++17+)
template <typename... Args>
void print_fold(Args... args) {
(std::cout << ... << args) << std::endl;
}

// 带分隔符的折叠表达式
template <typename... Args>
void print_with_separator(Args... args) {
((std::cout << args << ", "), ...);
std::cout << std::endl;
}

模板模板参数

模板模板参数允许将模板作为另一个模板的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 模板模板参数的基本形式
template <template <typename> class Container, typename T>
void process_container(Container<T>& container) {
for (auto& item : container) {
std::cout << item << " ";
}
std::cout << std::endl;
}

// 使用标准容器
template <template <typename, typename> class Container, typename T, typename Allocator>
void process_std_container(Container<T, Allocator>& container) {
for (auto& item : container) {
std::cout << item << " ";
}
std::cout << std::endl;
}

// 实际使用
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<double> lst = {1.1, 2.2, 3.3};

process_std_container(vec); // 处理vector
process_std_container(lst); // 处理list

别名模板

别名模板是C++11引入的特性,它允许为模板创建别名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 基本别名模板
template <typename T>
using Vec = std::vector<T>;

// 使用别名模板
Vec<int> int_vector = {1, 2, 3};
Vec<double> double_vector = {1.1, 2.2, 3.3};

// 带默认参数的别名模板
template <typename T = int>
using Matrix = std::vector<std::vector<T>>;

// 使用带默认参数的别名模板
Matrix<> int_matrix; // 等价于 std::vector<std::vector<int>>
Matrix<double> double_matrix; // 等价于 std::vector<std::vector<double>>

// 别名模板与模板模板参数结合
template <typename T, template <typename> class Container>
struct MyContainer {
Container<T> data;
};

template <typename T>
using MyVectorContainer = MyContainer<T, Vec>;

MyVectorContainer<int> container;

模板的性能优化

  1. 模板代码膨胀

    • 原因:为每个模板实例生成单独的代码
    • 影响:增加可执行文件大小和编译时间
    • 解决方案:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
           // 使用类型擦除减少代码膨胀
      template <typename T>
      class TypeErasedContainer {
      private:
      struct Concept {
      virtual ~Concept() = default;
      virtual void push_back(const void*) = 0;
      virtual void* get(size_t) = 0;
      };

      template <typename U>
      struct Model : Concept {
      std::vector<U> data;

      void push_back(const void* value) override {
      data.push_back(*static_cast<const U*>(value));
      }

      void* get(size_t index) override {
      return &data[index];
      }
      };

      std::unique_ptr<Concept> impl;

      public:
      template <typename U>
      TypeErasedContainer() : impl(std::make_unique<Model<U>>()) {}

      template <typename U>
      void push_back(U value) {
      static_cast<Model<U>*>(impl.get())->push_back(&value);
      }

      template <typename U>
      U& get(size_t index) {
      return *static_cast<U*>(static_cast<Model<U>*>(impl.get())->get(index));
      }
      };
  2. 编译时间优化

    • 预编译头文件:将常用模板定义放入预编译头
    • 显式实例化:减少重复实例化
    • 模板拆分:将模板声明和定义分离
    1
    2
    3
    4
    5
    6
    7
       // 显式实例化声明(.h文件)
    extern template class std::vector<int>;
    extern template class std::vector<double>;

    // 显式实例化定义(.cpp文件)
    template class std::vector<int>;
    template class std::vector<double>;
  3. 运行时性能

    • 内联展开:模板函数容易被编译器内联
    • 编译期计算:使用constexpr和模板元编程
    • 类型特化:为特定类型提供优化实现

现代C++中的模板特性

  1. C++11模板改进

    • 可变参数模板
    • 别名模板
    • 模板默认参数
    • 外部模板实例化
  2. C++14模板改进

    • 泛型lambda表达式
    • 返回类型推导
    • 变量模板
    1
    2
    3
    4
    5
    6
    7
       // 变量模板(C++14+)
    template <typename T>
    constexpr T pi = T(3.14159265358979323846);

    // 使用变量模板
    double area = pi<double> * radius * radius;
    float circumference = 2 * pi<float> * radius;
  3. C++17模板改进

    • 折叠表达式
    • 模板参数推导(类模板参数推导)
    • 内联变量
    • if constexpr
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
       // 类模板参数推导(C++17+)
    std::pair p(1, 2.5); // 推导为 std::pair<int, double>
    std::vector v = {1, 2, 3}; // 推导为 std::vector<int>

    // if constexpr(C++17+)
    template <typename T>
    void process(T t) {
    if constexpr (std::is_integral_v<T>) {
    std::cout << "Integral: " << t << std::endl;
    } else if constexpr (std::is_floating_point_v<T>) {
    std::cout << "Floating point: " << t << std::endl;
    } else {
    std::cout << "Other type" << std::endl;
    }
    }
  4. C++20模板改进

    • 概念(Concepts)
    • 约束模板参数
    • 模块(Modules)
    • 协程(Coroutines)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
       // 概念(C++20+)
    template <typename T>
    concept Integral = std::is_integral_v<T>;

    template <typename T>
    concept FloatingPoint = std::is_floating_point_v<T>;

    // 约束模板参数
    template <Integral T>
    T add(T a, T b) {
    return a + b;
    }

    template <FloatingPoint T>
    T add(T a, T b) {
    return a + b;
    }

模板的最佳实践

  1. 模板设计原则

    • 单一职责:每个模板只负责一个功能
    • 可测试性:确保模板可以被有效测试
    • 可扩展性:设计灵活的模板接口
    • 性能考虑:避免不必要的模板实例化
  2. 模板编程技巧

    • 标签分发:使用标签类型选择不同的实现
    • SFINAE:基于替换失败的重载解析
    • CRTP: curiously recurring template pattern
    • 类型 traits:编译期类型信息获取
  3. 模板的错误处理

    • static_assert:编译期断言
    • 概念检查:使用C++20概念进行约束
    • 错误消息优化:提供清晰的错误信息
    1
    2
    3
    4
    5
    6
       // 改进错误消息
    template <typename T>
    void process(T t) {
    static_assert(std::is_integral_v<T>, "T must be an integral type");
    // 处理逻辑
    }
  4. 模板的文档和注释

    • 模板参数说明:详细说明每个模板参数的用途
    • 约束条件:说明模板参数的约束条件
    • 使用示例:提供模板的使用示例

模板元编程的高级技巧

  1. 编译期算法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
       // 编译期排序
    template <typename T, T... Ns>
    struct IntegerSequence {
    using type = IntegerSequence;
    static constexpr size_t size = sizeof...(Ns);
    };

    // 编译期整数序列生成
    template <size_t N, size_t... Is>
    struct MakeIntegerSequence : MakeIntegerSequence<N-1, N-1, Is...> {};

    template <size_t... Is>
    struct MakeIntegerSequence<0, Is...> : IntegerSequence<size_t, Is...> {};

    // 编译期最大值查找
    template <typename T, T... Ns>
    struct MaxValue;

    template <typename T, T N>
    struct MaxValue<T, N> : std::integral_constant<T, N> {};

    template <typename T, T N1, T N2, T... Ns>
    struct MaxValue<T, N1, N2, Ns...> :
    std::integral_constant<T, N1 > N2 ? MaxValue<T, N1, Ns...>::value : MaxValue<T, N2, Ns...>::value> {};
  2. 表达式模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
       // 表达式模板实现惰性计算
    template <typename T>
    class Vector {
    private:
    std::vector<T> data;

    public:
    Vector(size_t size) : data(size) {}

    T& operator[](size_t index) { return data[index]; }
    const T& operator[](size_t index) const { return data[index]; }
    size_t size() const { return data.size(); }
    };

    // 表达式模板基类
    template <typename T, typename Expr>
    class VectorExpr {
    public:
    T operator[](size_t index) const {
    return static_cast<const Expr&>(*this)[index];
    }

    size_t size() const {
    return static_cast<const Expr&>(*this).size();
    }
    };

    // 向量加法表达式
    template <typename T, typename Lhs, typename Rhs>
    class VectorAdd : public VectorExpr<T, VectorAdd<T, Lhs, Rhs>> {
    private:
    const Lhs& lhs;
    const Rhs& rhs;

    public:
    VectorAdd(const Lhs& l, const Rhs& r) : lhs(l), rhs(r) {}

    T operator[](size_t index) const {
    return lhs[index] + rhs[index];
    }

    size_t size() const {
    return lhs.size();
    }
    };

    // 运算符重载
    template <typename T, typename Lhs, typename Rhs>
    VectorAdd<T, Lhs, Rhs> operator+(const VectorExpr<T, Lhs>& lhs, const VectorExpr<T, Rhs>& rhs) {
    return VectorAdd<T, Lhs, Rhs>(static_cast<const Lhs&>(lhs), static_cast<const Rhs&>(rhs));
    }

    // 从表达式构造向量
    template <typename T, typename Expr>
    Vector<T> operator+(const Vector<T>& vec, const VectorExpr<T, Expr>& expr) {
    Vector<T> result(vec.size());
    for (size_t i = 0; i < vec.size(); ++i) {
    result[i] = vec[i] + expr[i];
    }
    return result;
    }
  3. 策略模式与模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
       // 使用模板实现策略模式
    template <typename SortStrategy, typename Container>
    void sort(Container& container) {
    SortStrategy::sort(container);
    }

    // 排序策略
    truct BubbleSort {
    template <typename Container>
    static void sort(Container& container) {
    // 冒泡排序实现
    for (size_t i = 0; i < container.size() - 1; ++i) {
    for (size_t j = 0; j < container.size() - i - 1; ++j) {
    if (container[j] > container[j + 1]) {
    std::swap(container[j], container[j + 1]);
    }
    }
    }
    }
    };

    truct QuickSort {
    template <typename Container>
    static void sort(Container& container) {
    // 快速排序实现
    if (container.size() <= 1) return;

    auto pivot = container[container.size() / 2];
    Container left, middle, right;

    for (auto& item : container) {
    if (item < pivot) left.push_back(item);
    else if (item == pivot) middle.push_back(item);
    else right.push_back(item);
    }

    sort<QuickSort>(left);
    sort<QuickSort>(right);

    container.clear();
    container.insert(container.end(), left.begin(), left.end());
    container.insert(container.end(), middle.begin(), middle.end());
    container.insert(container.end(), right.begin(), right.end());
    }
    };

    // 使用策略
    std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6};
    sort<BubbleSort>(vec); // 使用冒泡排序
    sort<QuickSort>(vec); // 使用快速排序

模板在标准库中的应用

  1. STL容器

    • std::vector<T>:动态数组
    • std::list<T>:双向链表
    • std::map<K, V>:关联容器
  2. 算法库

    • std::sort:排序算法
    • std::find:查找算法
    • std::transform:变换算法
  3. 智能指针

    • std::unique_ptr<T>:独占所有权指针
    • std::shared_ptr<T>:共享所有权指针
    • std::weak_ptr<T>:弱引用指针
  4. 函数对象

    • std::function<T>:通用函数包装器
    • std::bind:函数绑定
    • std::mem_fn:成员函数适配器

模板编程的未来发展

  1. C++23模板改进

    • 显式对象参数
    • 多维下标运算符
    • 静态运算符
  2. C++26及以后

    • 反射(Reflection)
    • 元类(Metaclasses)
    • 编译期字符串操作
  3. 模板编程的趋势

    • 更强大的编译期计算能力
    • 更简洁的语法
    • 更好的错误消息
    • 与现代C++特性的深度集成

模板是C++中最强大的特性之一,它不仅支持泛型编程,还为编译期计算和元编程提供了基础。随着C++标准的不断发展,模板的能力也在不断增强,为C++程序员提供了越来越多的工具和技巧。

掌握模板编程需要时间和实践,但它将为你打开C++编程的新境界,使你能够编写更灵活、更高效、更可维护的代码。
std::cout << “Base::value = “ << value << std::endl; // 可以访问,因为是保护继承
}
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

## 可变参数模板与模板特化

### 可变参数模板的实现

```cpp
// 递归终止条件
template <typename T>
void print(T value) {
std::cout << value << std::endl;
}

// 可变参数模板
template <typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " ";
print(rest...); // 递归调用
}

// 使用可变参数模板
print(1, 2.5, "Hello", true);

折叠表达式(C++17)

1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用折叠表达式简化可变参数处理
template <typename... Args>
void print(Args&&... args) {
(std::cout << ... << args) << std::endl;
}

// 带分隔符的折叠表达式
template <typename... Args>
void print_with_separator(Args&&... args) {
const char* sep = "";
((std::cout << sep << args), sep = ", ")...;
std::cout << std::endl;
}

模板特化

类模板特化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 主模板
template <typename T>
class Container {
public:
void add(T value) {
std::cout << "Adding value: " << value << std::endl;
}
};

// 特化版本
template <>
class Container<std::string> {
public:
void add(std::string value) {
std::cout << "Adding string: '" << value << "'" << std::endl;
}
};

// 偏特化版本
template <typename T>
class Container<T*> {
public:
void add(T* value) {
if (value) {
std::cout << "Adding pointer: " << *value << std::endl;
} else {
std::cout << "Adding null pointer" << std::endl;
}
}
};

函数模板特化

1
2
3
4
5
6
7
8
9
10
11
// 主模板
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}

// 特化版本
template <>
const char* max<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) > 0 ? a : b;
}

概念(Concepts)与模板约束

自定义概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <concepts>

// 自定义概念
template <typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

template <typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};

template <typename T>
concept Sortable = requires(T a, T b) {
{ a < b } -> std::convertible_to<bool>;
{ a > b } -> std::convertible_to<bool>;
{ a == b } -> std::convertible_to<bool>;
};

// 使用概念约束模板参数
template <Numeric T>
T sum(T a, T b) {
return a + b;
}

template <Hashable T>
void hash_value(T value) {
std::size_t hash = std::hash<T>{}(value);
std::cout << "Hash: " << hash << std::endl;
}

template <Sortable T>
void sort_values(std::vector<T>& values) {
std::sort(values.begin(), values.end());
}

概念的组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 组合多个概念
template <typename T>
concept NumericAndSortable = Numeric<T> && Sortable<T>;

template <typename T>
concept StringLike =
std::same_as<T, std::string> ||
std::same_as<T, std::string_view> ||
std::same_as<T, const char*>;

// 使用组合概念
template <NumericAndSortable T>
T find_min(const std::vector<T>& values) {
return *std::min_element(values.begin(), values.end());
}

template <StringLike T>
std::string to_string(T value) {
if constexpr (std::same_as<T, const char*>) {
return std::string(value);
} else {
return std::string(value);
}
}

C++20模板增强

模板Lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

int main() {
// 模板lambda
auto add = []<typename T>(T a, T b) {
return a + b;
};

// 使用不同类型
std::cout << "Add integers: " << add(1, 2) << std::endl;
std::cout << "Add doubles: " << add(1.5, 2.5) << std::endl;
std::cout << "Add strings: " << add(std::string("Hello"), std::string(" World")) << std::endl;

// 带约束的模板lambda
auto multiply = []<typename T>(T a, T b) requires std::is_arithmetic_v<T> {
return a * b;
};

std::cout << "Multiply integers: " << multiply(3, 4) << std::endl;
std::cout << "Multiply doubles: " << multiply(2.5, 4.0) << std::endl;

// 可变参数模板lambda
auto print = []<typename... Args>(Args&&... args) {
(std::cout << ... << args) << std::endl;
};

print("The answer is ", 42, ", and pi is ", 3.14159);

return 0;
}

带requires表达式的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <concepts>
#include <iostream>

// 使用requires表达式约束模板参数
template <typename T>
requires std::integral<T> || std::floating_point<T>
T absolute(T value) {
return value < 0 ? -value : value;
}

// 结合概念使用
template <std::integral T>
T square(T value) {
return value * value;
}

// 带多个约束的模板
template <typename T>
requires std::copyable<T> && requires(T a, T b) {
{ a + b } -> std::same_as<T>;
{ a * b } -> std::same_as<T>;
}
T sum_and_product(T a, T b) {
return a + b * a;
}

int main() {
std::cout << "Absolute value of -5: " << absolute(-5) << std::endl;
std::cout << "Absolute value of -3.14: " << absolute(-3.14) << std::endl;

std::cout << "Square of 5: " << square(5) << std::endl;

std::cout << "Sum and product of 3 and 4: " << sum_and_product(3, 4) << std::endl;

return 0;
}

C++20模板增强的优点

  1. 更简洁的语法:模板lambda简化了泛型lambda的写法
  2. 更强的表达能力:requires表达式提供了更灵活的模板约束方式
  3. 更好的错误信息:当模板参数不满足约束时,提供更清晰的错误信息
  4. 更高的代码可读性:使用概念和requires表达式使模板代码更易于理解
  5. 更好的编译性能:编译器可以更早地检查模板参数的有效性

模板元编程高级技巧

编译期类型列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 类型列表
template <typename... Types>
struct TypeList {};

// 类型列表操作
template <typename List>
struct Length;

template <typename... Types>
struct Length<TypeList<Types...>> {
static constexpr size_t value = sizeof...(Types);
};

template <typename List, size_t Index>
struct TypeAt;

template <typename T, typename... Types>
struct TypeAt<TypeList<T, Types...>, 0> {
using type = T;
};

template <typename T, typename... Types, size_t Index>
struct TypeAt<TypeList<T, Types...>, Index> {
using type = typename TypeAt<TypeList<Types...>, Index - 1>::type;
};

// 使用类型列表
using MyTypes = TypeList<int, double, std::string>;
static constexpr size_t size = Length<MyTypes>::value; // 3
using SecondType = TypeAt<MyTypes, 1>::type; // double

编译期算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 编译期查找
template <typename List, typename T>
struct Contains;

template <typename T>
struct Contains<TypeList<>, T> {
static constexpr bool value = false;
};

template <typename T, typename... Types>
struct Contains<TypeList<T, Types...>, T> {
static constexpr bool value = true;
};

template <typename U, typename T, typename... Types>
struct Contains<TypeList<U, Types...>, T> {
static constexpr bool value = Contains<TypeList<Types...>, T>::value;
};

// 编译期过滤
template <typename List, template <typename> typename Predicate>
struct Filter;

template <template <typename> typename Predicate>
struct Filter<TypeList<>, Predicate> {
using type = TypeList<>;
};

template <typename T, typename... Types, template <typename> typename Predicate>
struct Filter<TypeList<T, Types...>, Predicate> {
using Rest = typename Filter<TypeList<Types...>, Predicate>::type;
using type = std::conditional_t<
Predicate<T>::value,
TypeList<T, Rest>,
Rest
>;
};

// 使用编译期算法
using FilteredTypes = Filter<MyTypes, std::is_arithmetic>::type; // TypeList<int, double>
static constexpr bool has_string = Contains<MyTypes, std::string>::value; // true

策略模式与模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 策略接口
template <typename T>
class SortStrategy {
public:
virtual void sort(std::vector<T>& data) = 0;
virtual ~SortStrategy() = default;
};

// 具体策略
template <typename T>
class QuickSort : public SortStrategy<T> {
public:
void sort(std::vector<T>& data) override {
std::sort(data.begin(), data.end());
std::cout << "Sorted with QuickSort" << std::endl;
}
};

template <typename T>
class BubbleSort : public SortStrategy<T> {
public:
void sort(std::vector<T>& data) override {
// 简化的冒泡排序
for (size_t i = 0; i < data.size(); i++) {
for (size_t j = 0; j < data.size() - i - 1; j++) {
if (data[j] > data[j + 1]) {
std::swap(data[j], data[j + 1]);
}
}
}
std::cout << "Sorted with BubbleSort" << std::endl;
}
};

// 策略选择器(模板版本)
template <typename T, template <typename> typename SortAlgorithm>
class Sorter {
public:
void sort(std::vector<T>& data) {
SortAlgorithm<T> algorithm;
algorithm.sort(data);
}
};

// 使用策略模式
std::vector<int> data = {5, 2, 8, 1, 9};
Sorter<int, QuickSort> quickSorter;
quickSorter.sort(data);

Sorter<int, BubbleSort> bubbleSorter;
bubbleSorter.sort(data);

模板

模板是C++中实现泛型编程的重要机制,它允许编写与类型无关的代码。

函数模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 函数模板示例
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}

// 显式实例化
template int max<int>(int, int);
template double max<double>(double, double);

int main() {
// 隐式实例化
int i = max(10, 20);
double d = max(3.14, 2.71);
std::string s = max(std::string("hello"), std::string("world"));

std::cout << "Max int: " << i << std::endl;
std::cout << "Max double: " << d << std::endl;
std::cout << "Max string: " << s << std::endl;

return 0;
}

类模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 类模板示例
template<typename T>
class Stack {
private:
std::vector<T> elements;

public:
void push(const T& item) {
elements.push_back(item);
}
void pop() {
if (!empty()) {
elements.pop_back();
}
}
T& top() {
return elements.back();
}
bool empty() const {
return elements.empty();
}
size_t size() const {
return elements.size();
}
};

int main() {
// 使用int类型的Stack
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;

// 使用string类型的Stack
Stack<std::string> stringStack;
stringStack.push("hello");
stringStack.push("world");
std::cout << "Top: " << stringStack.top() << std::endl;
stringStack.pop();
std::cout << "Top after pop: " << stringStack.top() << std::endl;

return 0;
}

模板特化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 模板特化示例
template<typename T>
class MyClass {
public:
void print() {
std::cout << "General template" << std::endl;
}
};

// 特化版本
template<>
class MyClass<int> {
public:
void print() {
std::cout << "Specialized for int" << std::endl;
}
};

// 特化版本
template<>
class MyClass<std::string> {
public:
void print() {
std::cout << "Specialized for string" << std::endl;
}
};

int main() {
MyClass<double> d;
MyClass<int> i;
MyClass<std::string> s;

d.print(); // 输出 General template
i.print(); // 输出 Specialized for int
s.print(); // 输出 Specialized for string

return 0;
}

标准模板库(STL)

STL是C++标准库的一部分,提供了一系列的容器、迭代器、算法和函数对象,是代码重用的重要工具。

容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 容器示例
#include <vector>
#include <list>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

int main() {
// 向量
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6);

// 列表
std::list<int> lst = {1, 2, 3, 4, 5};
lst.push_back(6);
lst.push_front(0);

// 映射
std::map<std::string, int> map;
map["one"] = 1;
map["two"] = 2;

// 集合
std::set<int> set = {1, 2, 3, 4, 5};
set.insert(6);

// 无序映射
std::unordered_map<std::string, int> umap;
umap["one"] = 1;
umap["two"] = 2;

// 无序集合
std::unordered_set<int> uset = {1, 2, 3, 4, 5};
uset.insert(6);

return 0;
}

算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 算法示例
#include <algorithm>
#include <vector>

int main() {
std::vector<int> vec = {5, 2, 8, 1, 9, 3};

// 排序
std::sort(vec.begin(), vec.end());

// 查找
auto it = std::find(vec.begin(), vec.end(), 5);
if (it != vec.end()) {
std::cout << "Found 5 at position: " << std::distance(vec.begin(), it) << std::endl;
}

// 计数
int count = std::count(vec.begin(), vec.end(), 5);
std::cout << "Count of 5: " << count << std::endl;

// 最大值
auto maxIt = std::max_element(vec.begin(), vec.end());
std::cout << "Max element: " << *maxIt << std::endl;

// 最小值
auto minIt = std::min_element(vec.begin(), vec.end());
std::cout << "Min element: " << *minIt << std::endl;

// 反转
std::reverse(vec.begin(), vec.end());

return 0;
}

迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 迭代器示例
#include <vector>

int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};

// 正向迭代器
std::cout << "Forward iteration: " << std::endl;
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

// 反向迭代器
std::cout << "Reverse iteration: " << std::endl;
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

// 常量迭代器
std::cout << "Constant iteration: " << std::endl;
for (auto it = vec.cbegin(); it != vec.cend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

return 0;
}

函数对象

函数对象是重载了函数调用运算符()的类的实例,可以像函数一样被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 函数对象示例
class Add {
private:
int value;

public:
Add(int v) : value(v) {}

int operator()(int x) const {
return x + value;
}
};

class Multiply {
private:
int factor;

public:
Multiply(int f) : factor(f) {}

int operator()(int x) const {
return x * factor;
}
};

int main() {
Add add5(5);
int result1 = add5(10); // 调用operator()
std::cout << "10 + 5 = " << result1 << std::endl;

Multiply multiply3(3);
int result2 = multiply3(10); // 调用operator()
std::cout << "10 * 3 = " << result2 << std::endl;

// 函数对象作为参数
std::vector<int> vec = {1, 2, 3, 4, 5};
std::transform(vec.begin(), vec.end(), vec.begin(), add5);

std::cout << "After adding 5: " << std::endl;
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

Lambda 表达式(C++11+)

Lambda表达式是C++11引入的匿名函数对象,提供了一种简洁的方式来定义函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Lambda表达式示例
int main() {
// 基本lambda
auto add = [](int a, int b) { return a + b; };
std::cout << "5 + 3 = " << add(5, 3) << std::endl;

// 带捕获的lambda
int x = 10;
auto addX = [x](int a) { return a + x; };
std::cout << "5 + x = " << addX(5) << std::endl;

// 引用捕获
auto addXRef = [&x](int a) { return a + x; };

// 捕获所有变量
auto addAll = [=](int a) { return a + x; };

// 可变lambda
auto increment = [x]() mutable { return ++x; };

// lambda作为参数
std::vector<int> vec = {5, 2, 8, 1, 9, 3};
std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });

std::cout << "Sorted in descending order: " << std::endl;
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

代码重用的最佳实践

1. 选择合适的代码重用机制

  • 继承:适用于”is-a”关系,需要多态的场景
  • 组合:适用于”has-a”关系,需要松耦合的场景
  • 模板:适用于泛型编程,需要与类型无关的代码
  • 函数对象:适用于需要状态的函数
  • Lambda表达式:适用于简短的、一次性的函数

2. 遵循设计原则

  • 单一职责原则:每个类只负责一项功能
  • 开放-封闭原则:对扩展开放,对修改封闭
  • 里氏替换原则:子类应该能够替换父类
  • 接口隔离原则:客户端不应该依赖它不使用的接口
  • 依赖倒置原则:依赖于抽象,而不是具体实现

3. 合理使用STL

  • 选择合适的容器:根据访问模式和性能需求选择容器
  • 使用算法:优先使用STL算法而不是手写循环
  • 使用迭代器:通过迭代器访问容器元素
  • 使用函数对象和lambda:简化代码,提高可读性

4. 代码组织

  • 模块化:将相关功能组织到模块中
  • 命名空间:使用命名空间避免名称冲突
  • 头文件和源文件分离:声明和实现分离
  • 文档:为代码添加注释和文档

常见错误和陷阱

1. 继承滥用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 错误:滥用继承
class Person {
public:
void eat() {}
void sleep() {}
};

// 错误:Student不是一种特殊的Person,而是有额外的功能
class Student : public Person {
public:
void study() {}
};

// 正确:使用组合
class Student {
private:
Person person;

public:
void eat() { person.eat(); }
void sleep() { person.sleep(); }
void study() {}
};

2. 模板错误

1
2
3
4
5
6
7
8
9
10
11
12
// 错误:模板特化顺序
// 一般模板
template<typename T>
class MyClass {
};

// 错误:特化模板在使用后定义
template<>
class MyClass<int> {
};

// 正确:先定义特化模板,再使用

3. STL使用错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 错误:在遍历过程中修改容器
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it == 3) {
vec.erase(it); // 错误:迭代器失效
}
}

// 正确:使用erase的返回值
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end();) {
if (*it == 3) {
it = vec.erase(it); // 正确:更新迭代器
} else {
++it;
}
}

4. 函数对象错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 错误:函数对象没有正确实现
class Comparator {
public:
// 错误:缺少const
bool operator()(int a, int b) {
return a < b;
}
};

// 正确:添加const
class Comparator {
public:
bool operator()(int a, int b) const {
return a < b;
}
};

小结

本章介绍了C++中的代码重用机制,包括:

  1. 继承与组合:两种基本的代码重用方式,分别对应”is-a”和”has-a”关系
  2. 私有继承和保护继承:实现”is-implemented-in-terms-of”关系
  3. 模板:实现泛型编程,编写与类型无关的代码
  4. 标准模板库(STL):提供容器、算法、迭代器等工具
  5. 函数对象:重载函数调用运算符的类的实例
  6. Lambda表达式:C++11引入的匿名函数对象
  7. 代码重用的最佳实践:选择合适的机制、遵循设计原则、合理使用STL等
  8. 常见错误和陷阱:继承滥用、模板错误、STL使用错误等

代码重用是提高开发效率、减少代码冗余、提高代码质量的重要手段。通过合理使用C++提供的代码重用机制,可以编写出更简洁、更高效、更可维护的代码。在实际编程中,应根据具体情况选择合适的代码重用方式,遵循最佳实践,避免常见错误。

在后续章节中,我们将学习C++的其他高级特性,如异常处理、命名空间、模板特化等,进一步提高C++编程能力。