文章目录
- C++模板的优势
- C++模板的主要优势包括:
- 编写你的第一个模板
- 函数模板的使用
- 理解模板术语
-
-
-
-
-
- 模板由一个或多个形参参数化(到目前为止,我们看到的例子都只有一个形参)。这些形参称为模板形参,可以分为3类。
-
- 可以通过提供替代实现来特化模板。这些实现可以依赖于模板形参的性质特点。特化的目的是实现优化或减少代码膨胀。特化有以下两种形式。
-
- 编译器通过将模板定义中的模板形参替换成实参,从而自模板生成代码的过程称为**模板实例化【template instantiation】** 。模板实例化有以下两种形式。
-
-
-
- 模板的优缺点
-
-
-
-
- 优点:
- 缺点:
-
-
-
C++模板的优势
c++是一门支持多种编程范式的语言,包括:
- 过程式编程(Procedural Programming):基于函数和过程的结构化编程。
- 面向对象编程(OOP):通过类、继承和多态实现模块化与复用。
- 泛型编程(Generic Programming):借助模板编写类型无关的代码,提高代码复用性和灵活性。
- 函数式编程(Functional Programming):使用不可变数据和高阶函数,提升代码可测试性和并发能力。
- 元编程(Metaprogramming):利用编译期计算优化程序,提高运行效率。在这些范式中,模板技术是C++的核心特性,它赋予C++强大的泛型编程能力,使代码适用于多种数据类型,而不需要冗余编写。例如,标准模板库(STL)的容器(如std::vector、std:map)和算法((如std::sort、std::find)均依赖模板实现。
C++模板的主要优势包括:
- 编译时多态(Compile-time Polymorphism):相比运行时多态(如继承与虚函数),模板允许编译期进行类型推导和优化,从而提高执行效率。
- 编译时计算(Compile-time Computation):利用模板元编程(TMP),C++能在编译期执行计算,减少运行时开销。例如,std::integral_constant 和std::conditional可用于选择编译期代码路径。
- 代码复用:模板减少了重复代码,提高了通用性。例如,std:enableif可用于 SFINAE(替换失败非错误),实现条件编译。
模板的强大使其在现代C++开发中占据重要地位,特别是在高性能计算、游戏开发、底层系统编程等领域。掌握模板不仅能提升代码质量,还能帮助程序员深入理解C++语言的底层机制。
编写你的第一个模板
- 这是一个比较两个值大小的函数模板
cpp
//函数模板
template<typename T>
T max(T const &a, T const &b)
{
return a > b ? a : b;
}
//类模板
template<typename T>
struct vector
{
vector();
void push_back(T const &value);
private:
T* data;
}
//变量模板
template<typename T>
constexpr T NewLine = T('\n');
- T成为类型模板形参,由语法template< typename T >或者template< class T >引入。请记住T是形参,因此它可以有任何名称。【在后续的文章中我们会了解更多关于模板形参的内容】
函数模板的使用
cpp
struct foo{};
int main
{
foo f1,f2;
max(1,2); //正确,比较int
max(1.0,2.0); //正确,比较double
max(f1,f2); //错误,foo没有重载operator>
}
- 在以上代码片段中,首先传入两个整数来调用max,这是可以的,因为int 类型有operator>。这将生成一个重载int max(int const a, int const b)。之后,传入两个double 来调用max,这也是正确的,因为double类型支持operator>。因此,编译器将生成另一个重载 double max(double const a, double const b)。但是,对max的第三次调用将生成一个编译错误,因为foo类型没有重载operator>。
- 需要指出,调用max函数的完整语法如下。
- max< int >(1,2);
- max< double >(1.0, 2.0);
- max< foo >(f1,f2);
- 这里编译器推导出模板形参类型,写出类型反而变得多余。但是,在某些情况下编译器无法进行推导,那你就需要用以上语法明确指定类型。
理解模板术语
到目前为止,我们用的都是通用术语"模板"。其实,上文编写的模板可以用4个不同的术语分别描述。
- 函数模板(function template)是指模板化的函数。之前看到的max模板就是一个例子。
- 类模板(class template)是指模板化的类((可以使用关键字class、struct或union 定义)。
- 变量模板(variable template)是指模板化的变量。
- 别名模板(alias template)是指模板化的类型别名。
模板由一个或多个形参参数化(到目前为止,我们看到的例子都只有一个形参)。这些形参称为模板形参,可以分为3类。
- 类型模板形参(type template parameters),如template< typename T>, 形参表示的是使用模板时指定的类型。
- 非类型模板形参(non-type template parameters),如template< size_t N>或template< auto N>,每个形参必须有结构化类型,包括整型、浮点型(C++20支持)、指针类型、枚举类型、左值引用类型等。
- 模板模板形参(template template parameters),如template< typename K, typename V, template< typename> typename C>,形参的类型是另一个模板。
可以通过提供替代实现来特化模板。这些实现可以依赖于模板形参的性质特点。特化的目的是实现优化或减少代码膨胀。特化有以下两种形式。
- 部分特化(partial specialization): 只为部分模板形参提供替代实现。
- (显示)完全特化[(explicit)full specialization]: 为所有模板形参提供特化实现。
编译器通过将模板定义中的模板形参替换成实参,从而自模板生成代码的过程称为模板实例化【template instantiation】 。模板实例化有以下两种形式。
- 隐式实例化(implicit instantiation): 编译器由于代码中用到了模板而对其进行实例化。只会为实际使用到的组合或者实参进行实例化。
- 显式实例化(explicit instantiation): 这种方式要显式地告诉编译器需要实例化哪些模板,即便这些实例化在代码中没有显式使用。这在创建库文件很有用,因为未实例化的模板不会被放入目标文件中。
模板的优缺点
优点:
- 模板可以帮助我们避免编写重复的代码。
- 模板有利于创建提供算法和类型的泛型库,如标准C++库(有时被错误地称为STL),这些库可以在许多应用程序中使用,不拘类型。
- 使用模板可以产生更少且更好的代码。例如使用标准库中的算法可以帮助编写更短小的代码,这些代码往往更易于理解和维护,此外,由于这些算法在开发和测试中投入了大量精力,它们通常会更加健壮。
缺点:
- 语法被认为很复杂而笨拙,尽管稍加练习就不会真正成为开发和使用模板的障碍。
- 与模板相关的编译错误常常长且晦涩,很难找出错误的原因。较新版本的C++编译器在简化这类错误方面有所进步,但通常仍然是一个重要的问题。C++20标准中引入的概念(concepts)也被看作一种努力,旨在提供更好的编译错误诊断。它们会增加编译时间,因为模板完全在头文件中实现。每当对模板进行更改时,包含该头文件的所有翻译单元都必须重新编译。
- 模板库作为一个或多个头文件的集合提供,必须与使用它们的代码一起编译。模板在头文件中实现的另一个缺点是缺乏信息隐藏。整个模板代码都在头文件中可读。库开发者通常会使用诸如detail或details等名空间来包含应放在库内部的代码,它们不应该被使用库的人直接调用。由于未使用的代码不会被编译器实例化,这些代码就可能更难验证。因此,在编写单元测试时,务必确保良好的代码覆盖率。对于库尤其如此。
- 注:尽管缺点列表看起来更长,但使用模板并非坏事,也不应该被回避。相反,模板是C++语言的一个强大特性。模板并不总是被正确理解,有时也会被误用或滥用。但是,请审慎地使用,模板确实有不可否认的优点。