学习C++模版完全是因为Nvidia。之前看一个Nvidia的视频,里面提到了要好好学习C++知识;最近学cutlass又用到了大量的C++模版;在知乎上看有人说看了 C++ Templates - The Complete Guide, 2nd Edition 之后神清气爽,惊叹还有这种操作。于是,C++ template,启动!
雾里看花:真正意义上的理解 C++ 模板
我看的入坑文章,很有意思!
四个主题:
- 代码生成 (Code Generation)
- TMP (Template meta programming 模板元编程)
- 类型约束 (Type Constraint)
- 通过requires我们能阻止编译错误的传播
- 编译时计算 (Compile-time Computing)
- C++ 允许在一个函数前面直接加上constexpr关键字修饰。表示这个函数既可以在运行期调用,也可以在编译期调用,而函数本身的内容几乎不需要任何改变。这样一来,我们可以直接把运行期的代码复用到编译期。
- 操纵类型 (Type Manipulation)
- 在 C++ 中类型不是一等公民,只能作为模板参数,在涉及到类型相关的计算的时候,我们就不得不编写繁琐的模板元代码
- 类型约束 (Type Constraint)
模板设计之初的意图并不是实现后面这三个功能,但是最后却通过一些奇怪的 trick 实现了这些功能,代码写起来也比较晦涩难懂,所以一般叫做元编程。
Code Generation
在加入模板之前,我们只能通过宏来模拟泛型。
1 |
|
把普通函数中的类型替换成宏参数,通过宏符号拼接来为不同的类型参数生成不同的名字。再通过IMPL宏来为特定的函数生成定义,这个过程可以叫做实例化 (instantiate)。
而这主要有如下几个缺点
- 代码可读性差,宏的拼接和代码逻辑耦合,报错信息不好阅读
- 很难调试,打断点只能打到宏展开的位置,而不是宏定义内部
- 需要显式写出类型参数,参数一多起来就会显得十分冗长
- 必须手动实例化函数定义,在较大的代码库中,往往一个泛型可能有几十个实例化,全部手动写出过于繁琐
这些问题,在模板中都被解决了: 1
2
3
4
5
6
7
8
9
10
11
12template <typename T>
T add(T a, T b) {
return a + b;
}
template int add<>(int, int); // explicit instantiation
int main() {
add(1, 2); // auto deduce T
add(1.0f, 2.0f); // implicit instantiation
add<float>(1, 2); // explicitly specify T
}
- 模板就是占位符,不需要字符拼接,和普通的代码别无二致,仅仅多了一项模板参数声明
- 报错和调试都能准确的指向模板定义的位置,而不是模板实例化的位置
- 支持模板参数自动推导,不需要显式写出类型参数,同时也支持显式指定类型参数
- 支持隐式实例化 (implicit instantiation),即由编译器自动实例化使用到的函数。也支持显式实例化 (explicit instantiation),即手动实例化。
除此之外,还有诸如偏特化 (partial specialization),全特化 (full specialization),可变模板参数 (variadic template),变量模板 (variable template) 等等一系列特性,这些仅凭宏都是做不到的。正是由于模板的出现,才使用 STL 这样的泛型库的实现成为可能。
小试牛刀 Templates in C++
C++ template is a powerful tool that allows you to write a generic code that can work with any data type. The idea is to simply pass the data type as a parameter so that we don't need to write the same code for different data types.
Templates are expanded at compiler time.
Templates vs Function Overloading
在 C++中,函数重载和函数模板都允许我们创建可以操作不同类型数据的函数。虽然它们看起来相似,但它们用于不同的目的。
当你需要对不同类型或数量的输入执行相似操作时,使用函数重载。
当你需要一个单一的函数来处理不同的数据类型,而无需为每种类型重写函数时,使用函数模板。
Feature | Function Overloading | Function Templates |
---|---|---|
Purpose | Define multiple functions with the same name but different parameters | Define a single function to work with different data types |
Syntax | void functionName(int a); void functionName(double a); void functionName(int a, int b); |
template void functionName(T a); template T functionName(T a, T b); |
Code Duplication | Multiple functions, potentially similar code | Single function, no code duplication |
Type Safety | Checked at compile-time | Checked at compile-time |
Readability | Clear which function is called based on parameters | More abstract, but cleaner for multiple types |
Flexibility | Limited to predefined function signatures | More flexible, works with any data type |
Maintenance | Harder to maintain due to multiple functions | Easier to maintain, single function definition |
结论:
Function overloading and function templates are both important features of C++ that allow for flexible and reusable code. We can use function overloading when we have similar functions that operate on different types or numbers of parameters and use function templates when we want a single function to work with different data types, reducing code duplication and increasing flexibility.
- 当我们有相似但操作不同类型或参数数量的函数时,可以使用函数重载;
- 当我们希望一个函数能处理不同的数据类型,减少代码重复并提高灵活性时,可以使用函数模板。
C++ Templates - The Complete Guide, 2nd Edition
在纷繁多变的世界里茁壮成长:C++ 2006–2020
学习期间解锁了 C++ 之父 Bjarne Stroustrup 的 HOPL4 论文。
设计 C++ 是为了回答这样的一个问题:
- How do you directly manipulate hardware and also support efficient high-level abstraction?
- 如何直接操作硬件,同时又支持高效、高级的抽象?
Abstractions are represented in code as functions, classes, templates, concepts, and aliases.
- 抽象在代码中体现为函数、类、模板、概念和别名。