C++ 模板特化2025年03月17日 | 阅读 9 分钟 为什么要使用模板?C++ 要求我们为变量、函数和其他实体使用特定的类型来声明。然而,对于不同的类型,很多代码看起来是相同的。特别是如果我们实现算法,比如快速排序,或者数据结构,比如链表或二叉树,对于不同的类型,代码看起来是相同的,尽管使用的类型不同。假设我们的编程语言不支持这种特殊的语言特性。在这种情况下,我们只有糟糕的选择:
如果我们来自 C、Java 或类似语言,我们以前做过一些或全部这些事情。然而,每种方法都有其缺点:
模板是解决这个问题的方案,没有这些缺点。它们是为一种或多种尚未指定的类型编写的函数或类。当我们使用模板时,我们显式或隐式地将类型作为参数传递。由于模板是语言特性,我们拥有完整的类型检查和作用域支持。在今天的程序中,模板使用非常广泛。例如,在 C++ 标准库中,几乎所有代码都是模板代码。该库提供了用于对指定类型的对象和值进行排序的算法,用于管理选定类型元素的(所谓的容器)数据结构,用于参数化字符样式的字符串等等。然而,这仅仅是个开始。模板还允许我们参数化行为、优化代码和参数化信息。这将在后面的章节中介绍。让我们先从一些简单的模板开始。 类模板的特化我们可以为特定的模板参数特化类模板。就像函数模板的重载一样,特化类模板允许我们为特定类型优化实现,或修复特定类型实例化类模板时的错误行为。然而,如果我们特化一个类模板,我们也必须特化所有成员函数。虽然可以特化单个成员函数,但一旦这样做,我们就不能再特化整个类了。 要特化类模板,我们必须使用 `template<>` 前缀声明类,并指定该类模板特化的类型。这些类型用作模板参数,并且必须直接跟在类名后面。 对于这些特化,任何成员函数的定义都必须作为“普通”成员函数来定义,其中 T 的每个出现都被替换为特化类型。 部分特化类模板可以部分特化。我们可以为特定情况指定特化的实现,但用户仍需定义一些模板参数。例如,对于以下类模板: 以下示例显示了哪个模板被哪个声明使用: 如果多个部分特化匹配的程度相同,则声明是模棱两可的。 函数模板函数模板是特殊的函数,可以处理通用类型。它允许我们创建一个函数模板,其功能可以适应多种类型或类,而无需为每种类型重复整个代码。 在 C++ 中,这可以通过模板参数来实现。模板参数是一种特殊的参数,可以用于将类型作为参数传递:就像常规函数参数可以用于将值传递给函数一样,模板参数也允许将类型传递给函数。这些函数模板可以像使用其他常规类型一样使用这些参数。 使用类型参数声明函数模板的格式是: 这两种原型之间的唯一区别是使用了 `class` 关键字或 `typename` 关键字。它们的使用是可区分的,因为这两种表达式具有相同的含义并且行为相似。 例如,要创建一个返回两个对象中较大者值的模板函数,我们可以使用: 在这里,我们创建了一个以 `myType` 作为其模板参数的模板函数。此模板参数代表一个尚未指定的类型,但可以在模板函数中使用,就像它是常规类型一样。如您所见,函数模板 `GetMax` 返回两个此尚未定义类型的参数中的较大者。要使用此函数模板,我们使用以下格式的函数调用: function_name <type> (parameters); 例如,要调用 `GetMax` 来比较两个 `int` 类型的整数值,我们可以编写: 当编译器遇到对模板函数的此调用时,它使用模板自动生成一个函数,将 `myType` 的每个实例替换为作为实际模板参数传递的类型(在此情况下为 `int`),然后调用它。编译器会自动执行此过程,并且对程序员是不可见的。 这是整个示例: 在这种情况下,我们使用 `T` 作为模板参数名而不是 `myType`,因为它更短,并且是一个非常常见的模板参数名。但您可以使用任何您喜欢的标识符。 我们在上面的示例中两次使用了函数模板 `GetMax()`。第一次是使用 `int` 类型的参数,第二次是使用 `long` 类型的参数。编译器每次都实例化并调用了适当版本的函数。 可以看到,在 `GetMax()` 模板函数中,类型 `T` 甚至被用来声明该类型的新对象: T result因此,当使用特定类型实例化函数模板时,结果将是一个与参数 `a` 和 `b` 类型相同的对象。 在这种通用类型 `T` 用作 `GetMax` 的参数的特定情况下,编译器可以自动找出需要实例化哪种数据类型,而无需在尖括号中显式指定(就像我们之前指定 `<int>` 和 `<long>` 一样)。所以我们可以写成: 由于 `i` 和 `j` 都是 `int` 类型,编译器可以自动确定模板参数只能是 `int`。这种隐式方法会产生相同的结果。 请注意,在这种情况下,我们调用了函数模板 `GetMax()`,而没有在尖括号 `<>` 中显式指定类型。编译器会自动确定每次调用需要什么类型。 因为我们的模板函数只包含一个模板参数(`class T`),并且函数模板本身接受两个参数,这两个参数都是 `T` 类型,所以我们不能用两个不同类型的对象作为参数来调用我们的函数模板。 这是不正确的,因为我们的 `GetMax` 函数模板期望两个相同类型的参数,而在这次调用中,我们使用了两种不同类型的对象。 我们还可以通过在尖括号之间指定更多模板参数来定义接受多个参数类型的函数模板。例如: 在这种情况下,我们的函数模板 `GetMin()` 接受两个不同类型的参数,并返回与传递的第一个参数(`T`)类型相同的对象。例如,在此声明之后,我们可以使用以下方式调用 `GetMin()`: 即使 `j` 和 `l` 类型不同,编译器仍然可以确定适当的实例化。 类模板 我们还可以编写类模板,以便类拥有可以使用模板参数作为类型的成员。例如: 我们刚刚定义的类用于存储任何有效类型的两个元素。例如,如果我们想声明一个类对象来存储两个 `int` 类型的整数值,值为 115 和 36,我们将编写: mypair myobject (115, 36); 同一个类也将用于创建对象来存储任何其他类型: mypair myfloats (3.0, 2.18); 上一个类模板中唯一的成员函数已在类声明本身中内联定义。如果我们为类模板声明之外的成员函数定义一个函数,我们必须始终在定义前加上 `template <...>` 前缀。 输出 ![]() 注意成员函数 `getmax` 定义的语法: 此声明包含三个 `T`:第一个是模板参数。第二个 `T` 指示函数返回的类型。第三个 `T`(尖括号之间的那个)也是一个要求:它指定此函数的模板参数也是类模板参数。 模板特化如果我们想在将特定类型作为模板参数传递时为模板定义不同的实现,我们可以声明该模板的特化。例如,我们有一个非常简单的类 `mycontainer`,它可以存储任何类型的单个元素,并且只有一个名为 `increase` 的成员函数,该函数增加其值。但是我们发现,当它存储 `char` 类型元素时,拥有一个具有 `uppercase` 成员函数的完全不同的实现会更方便,因此我们决定为该类型声明一个类模板特化。 输出 ![]() 下面是类模板特化中使用的语法: 首先,请注意我们在类模板名称前加上了一个空的 `template <>` 参数列表。这是为了明确声明它是一个模板特化。 但类模板名称后面的特化参数比这个前缀更重要。这个特化参数标识我们将为其声明模板类特化的类型(`char`)。请注意通用类模板和特化之间的区别: 第一行是通用模板,第二行是特化。当我们为模板类声明特化时,我们还必须定义其所有成员,即使是那些与通用模板类完全相同的成员,因为通用模板到特化的成员之间没有“继承”。 |
C++ 允许开发人员开发强大的应用程序,它被誉为市场上最强大、最灵活的编程语言之一。在众多 C++ 函数中,`wmemmove()` 是一种处理相似数组中宽度的块移动的有用技术。这是一个深入的教程……
阅读 6 分钟
在本文中,我们将讨论 C++ 中 array::fill() 和 array::swap() 之间的区别。但在讨论它们的区别之前,我们必须先了解 array::fill() 和 array::swap()。C++ 标准模板库 (STL) 的两个成员函数,它们与 std::array 模板类相关……
阅读 4 分钟
面向对象编程思想由通用、中级、区分大小写、平台无关的计算机语言 C++ 支持。C++ 编程语言由 Bjarne Stroustrup 于 1979 年在贝尔实验室开发。由于 C++ 是一种平台独立编程语言,它可以在各种操作系统上使用,...
阅读 4 分钟
在本文中,我们将讨论以及它们的特性和示例。在 C++ 语言中,关联数组将引用将键和值关联起来的数据结构。它们对于根据相应的键存储和检索值非常有效。这些关联数组是通过各种……实现的
阅读 4 分钟
在本教程中,我们将学习如何在 C++ 中创建计时器。秒表在按下键盘上的任意键时开始,在按下另一个键之前不会结束。让我们学习如何在 C++ 中创建计时器。我们将使用一个名为...的函数。
阅读9分钟
C++ 是一种功能强大的编程语言,以其效率和适应性而闻名。Multimap 容器是管理其大型标准模板库 (STL) 中多个键值对的有用工具。本次调查深入探讨了 multimap::count() 的细微差别,这是一个对于……至关重要的成员函数。
阅读 6 分钟
在 C++ 中,is_pod 是一种类型特征。它确定给定类型是否为普通旧数据 (POD)。POD 类型是简单的数据类型,可以直接在内存中存储和操作,无需特殊处理。它是 C++ 标准库的一部分...
阅读 3 分钟
partition point() 获取分区点:返回一个迭代器,指向范围 [first, last] 中第一个谓词 pred(predicate) 为 false 的分区元素,表示该元素的 the partition point。如同使用相同的输入调用了 partition 一样,该范围的元素必须已经...
阅读 4 分钟
在 C++ 中,“线程同步”是指用于同步多个线程执行的任务的方法和系统,确保它们平滑且在严格监控下运行。在多线程程序中,多个执行线程可以同时运行,访问共享资源并导致问题……
阅读 16 分钟
?本节将讨论 C++ 编程语言中两个或多个字符串的连接。字符串的连接意味着将两个或多个字符串组合起来,返回一个连接后的单个字符串。在连接字符串时,第二个字符串被添加到…
5 分钟阅读
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India