函数模板 您所在的位置:网站首页 powerpoint设计模板限定模板类型吗 函数模板

函数模板

2024-05-06 18:03| 来源: 网络整理| 查看: 265

函数模板 ​

本节将介绍函数模板

初识函数模板 ​

函数模板[1]不是函数,只有实例化[2]函数模板,编译器才能生成实际的函数定义。不过在很多时候,它看起来就像普通函数一样。

定义模板 ​

下面是一个函数模板,返回两个对象中较大的那个:

cpptemplate T max(T a,T b){ return a > b ? a : b; }

这应该很简单,即使我们还没有开始讲述函数模板的语法。

如果要声明一个函数模板,我们通常要使用:

cpptemplate< 形参列表 > 函数声明

我们前面示例中的形参列表是 typename T,关键字 typename 顾名思义,引入了一个类型形参。

类型形参是 T,也可以使用其他标识符作为类型形参名(T 或 Ty 等,是约定的惯例),你也可以在需要的时候自定义一些有明确意义的名字。在调用函数模板 max 时,根据传入参数,编译器可以推导出类型形参的类型,实例化函数模板。我们需要传入支持函数模板操作的类型,如 int 或 重载了 > 运算符的类。注意 max 的 return 这意味着我们的模板形参 T 还需要是可复制/移动的,以便返回。

C++17 之前,类型 T 必须是可复制或移动才能传递参数。C++17 以后,即使复制构造函数和移动构造函数都无效,因为 C++17 强制的复制消除,也可以传递临时纯右值。

因为一些历史原因,我们也可以使用 class 关键字来定义模板类型形参。所以先前的模板 max 可以等价于:

cpptemplate T max(T a,T b){ return a > b ? a : b; }

但是与类声明不同,在声明模板类型形参时,不能使用 struct。

使用模板 ​

下面展示了如何使用函数模板 max()

cpp#include template T max(T a, T b) { return a > b ? a : b; } struct Test{ int v_{}; Test() = default; Test(int v) :v_(v) {} bool operator>(const Test& t) const{ return this->v_ > t.v_; } }; int main(){ int a{ 1 }; int b{ 2 }; std::cout b ? a : b; }

如果我们 max(1, 2) 或者说 max(x,x),T 当然会是 int,但是函数形参类型会是 const int&。

不过我们需要注意,有不少情况是没有办法进行推导的:

cpp// 省略 max using namespace std::string_literals; int main(){ max(1, 1.2); // Error 无法确定你的 T 到底是要 int 还是 double max("luse"s, "乐"); // Error 无法确定你的 T 到底是要 std::string 还是 const char[N] }

那么我们如何处理这种错误呢?可以使用前面提到的显式指定函数模板的(T)类型。

cppmax(1, 1.2); max("luse"s, "乐");

又或者说显式类型转换:

cppmax(static_cast(1), 1.2);

但是 std::string 没有办法如此操作,我们可以显式的构造一个无名临时对象:

cppmax("luse"s, std::string("乐")); // Error 为什么?

此时就不是我们的 T 不明确了,而是函数模板 max 不明确,它会和标准库的 std::max 产生冲突,虽然我们没有使用 std::,但是根据 C++ 的查找规则,(实参依赖查找)ADL,依然可以查找到。

那么我们如何解决呢?很简单,进行有限定名字查找,即使用 :: 或 std:: 说明,你到底要调用 “全局作用域”的 max,还是 std 命名空间中的 max。

cpp::max("luse"s, std::string("乐"));万能引用与引用折叠 ​

所谓的万能引用(又称转发引用),即接受左值表达式那形参类型就推导为左值引用,接受右值表达式,那就推导为右值引用。

比如:

cpptemplate void f(T&&t){} int a = 10; f(a); // a 是左值表达式,f 是 f 但是它的形参类型是 int& f(10); // 10 是右值表达式,f 是 f 但它的形参类型是 int&&

被推导为 f 涉及到了特殊的推导规则:如果 P 是到无 cv 限定模板形参的右值引用(也就是转发引用)且对应函数的调用实参是左值,那么将到 A 的左值引用类型用于 A 的位置进行推导。

通过模板或 typedef 中的类型操作可以构成引用的引用,此时适用引用折叠(reference collapsing)规则:

右值引用的右值引用折叠成右值引用,所有其他组合均折叠成左值引用。cpptypedef int& lref; typedef int&& rref; int n; lref& r1 = n; // r1 的类型是 int& lref&& r2 = n; // r2 的类型是 int& rref& r3 = n; // r3 的类型是 int& rref&& r4 = 1; // r4 的类型是 int&&cpptemplate constexpr Ty&& forward(Ty& Arg) noexcept { return static_cast(Arg); } int a = 10; // 不重要 ::forward(a); // 返回 int&& 因为 Ty 是 int,Ty&& 就是 int&& ::forward(a); // 返回 int& 因为 Ty 是 int&,Ty&& 就是 int& ::forward(a); // 返回 int&& 因为 Ty 是 int&&,Ty&& 就是 int&&有默认实参的模板类型形参 ​

就如同函数形参可以有默认值一样,模板形参也可以有默认值。当然了,这里是“类型形参”(后面会讲非类型的)。

cpptemplate void f(); f(); // 默认为 f f(); // 显式指明为 fcppusing namespace std::string_literals; template RT max(const T1& a, const T2& b) { // RT 是 std::string return a > b ? a : b; } int main(){ auto ret = ::max("1", "2"s); std::cout decltype(true ? a : b){ return a > b ? a : b; }

这是 C++11 后置返回类型,它和我们之前用默认模板实参 RT 的区别只是稍微好看了一点吗?

不,它们的返回类型是不一样的,如果函数模板的形参是类型相同 true ? a : b 表达式的类型是 const T&;如果是 max(1, 2) 调用,那么也就是 const int&;而前面的例子只是 T 即 int(前面都是用模板类型参数直接构造临时对象,而不是有实际对象,自然如此,比如 T{})。

假设以 max(1,1.0) 调用,那么自然返回类型不是 const T&

cppmax(1, 2.0); // 返回类型为 double

下面的 max 定义也类似,涉及的规则不再强调。

使用 C++20 简写函数模板,我们可以直接再简化为:

cppdecltype(auto) max(const auto& a, const auto& b) { return a > b ? a : b; }

效果和上面使用后置返回类型的写法完全一样;C++14 引入了两个特性:

返回类型推导(也就是函数可以直接写 auto 或 decltype(auto) 做返回类型,而不是像 C++11 那样,只是后置返回类型。

decltype(auto) “如果返回类型没有使用 decltype(auto),那么推导遵循模板实参推导的规则进行”。我们上面的 max 示例如果不使用 decltype(auto),按照模板实参的推导规则,是不会有引用和 cv 限定的,就只能推导出返回 T 类型。

大家需要注意后置返回类型和返回类型推导的区别,它们不是一种东西,后置返回类型虽然也是写的 auto ,但是它根本没推导,只是占位。

模板的默认实参无处不在,比如标准库的 std::vector,std::string,当然了,这些都是类模板,我们以后会讲到。

非类型模板形参 ​

既然有”类型模板形参“,自然有非类型的,顾名思义,也就是模板不接受类型,而是接受值或对象。

cpptemplate void f() { std::cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有