赣州市南康区建设局网站,百度移动端排名软件,网站后台可以做两个管理系统么,淮南网站建设好一#xff0c;函数模板
1.基础概念
模板编程是C中泛型编程的基础。
一个模板可以是创建类或者函数的蓝图。
模板编程分两种#xff0c;分别是算法抽象的模板、数据抽象的模板。算法抽象的模板以函数模板为主#xff0c;数据抽象的模板以类模板为主。
基于函数模板生成的…一函数模板
1.基础概念
模板编程是C中泛型编程的基础。
一个模板可以是创建类或者函数的蓝图。
模板编程分两种分别是算法抽象的模板、数据抽象的模板。算法抽象的模板以函数模板为主数据抽象的模板以类模板为主。
基于函数模板生成的函数定义被称为模板的一个实例。
模板的定义以关键字template开始后跟一个由尖括号括起来的模板参数列表。 2.函数模板的简单样例
函数模板的开头template
定义模板参数的关键字typename
模板参数样例T1, T2
函数参数样例a, b
template typename T1, typename T2
void func(T1 a, T2 b)
{//process code
}
补充在C98标准添加关键字typename之前C也可以使用关键字class来为函数模板创建模板参数列表。 代码样例
template class T
void Swap(T a, T b)
{T temp;temp a;a b;b temp;
}
3.函数模板的实例化
函数模板的实例化是指编译器根据函数模板和具体的数据类型生成函数定义。
函数模板在实例化以后模板参数会变成具体的数据类型比如int, char等。
对于某一种具体的数据类型比如int无论以这个数据类型调用多少次函数模板最后只生成一次该类型的模板实例。
所以对于相同的数据类型第一次调用函数模板的时候才会生成实例后面再次调用的时候都是直接使用该实例。
当编译器遇到一个函数模板的定义时并不会马上生成相关代码只有当我们将函数模板实例化成一个函数定义时编译器才会生成代码。 代码样例
a.函数模板
template typename T
T add(T num1, T num2) {return (num1 num2);
}
b.函数模板的实例化
int result1 addint(2, 3);
double result2 adddouble(2.2, 3.3);
实例化过程的图示 在项目工程中我们通常将类的定义放在头文件中将类的成员函数的定义放在源文件中将普通函数的声明放在头文件中将普通函数的定义放在源文件中但是函数模板的规则和它们不一样。 为了让编译器为实例化后的函数模板生成代码编译器需要同时知道函数模板的声明和定义因此函数模板的定义也需要放在头文件中。 4.函数模板的引用传参 对于以下函数模板
template typename T
T larger(T a, T b)
{return a b ? a : b;
}
该函数模板实例化以后生成的函数需要按值传递的方式接收实参。
由于按值传送对象会导致不必要地复制这些对象因此推荐使用const引用的方式定义模板参数。
template typename T
const T larger(const T a, const T b)
{return a b ? a : b;
}
5.函数模板的返回类型推断
对于无返回值的函数模板可以把返回值类型写为void比如最开始提到的
template typename T1, typename T2
void func(T1 a, T2 b)
有的函数模板返回值类型和参数一致同为T比如
template typename T
T larger(T a, T b)
但是当返回值类型和参数不一致时得想办法让编译器可以推断返回值类型。
最简单的方式是使用auto关键字。
template typename T1, typename T2
auto larger(const T1 a, const T2 b)
{return a b ? a : b;
}
但是使用auto来推导函数的返回值类型时会默认去掉引用和const限定符因此以上方式会导致返回值发生不必要的复制。 因此为了让返回值被const修饰且采取引用的方式来传值需要显式地加上const 以上代码可以改为
template typename T1, typename T2
const auto larger(const T1 a, const T2 b)
{return a b ? a : b;
}
还有一种更好的方式C11标准引入了decltype关键字decltype相当于const auto因为decltype在做类型推导时不会去掉引用和const限定符。
但是decltype的用法不能像auto一样直接放在函数名前面。 decltype用法分两种
方式1.拖尾方式decltype(返回值相关代码)
template typename T1, typename T2
auto larger(T1 a, T2 b) - decltype(a b ? a : b)
{return a b ? a : b;
}
方式2.和auto关键字结合decltype(auto)
template typename T1, typename T2
decltype(auto) larger(T1 a, T2 b)
{return a b ? a : b;
}
第一种用法需要把返回值相关的代码逻辑重复写一遍第二种用法更简洁。 6.模板参数可以指定默认值
可以用具体的数据类型为模板参数指定默认值。
例如当函数经常使用int类型的参数时指定模板参数的默认值为int。
template typename T1int, typename T2
void func(T1 a, T2 b)
7.非类型的模板参数
模板参数分两种
1.类型模板参数
2.非类型模板参数 由尖括号括起来的模板参数列表中除了可以包含类型模板参数还可以包含非类型模板参数。
以上提到的typename T1, typename T2中的T1, T2都属于类型模板参数而int n, float m中的n, m都属于类型模板参数非类型模板参数。
类型模板参数经过实例化会变成具体类型。
非类型模板参数经过实例化会变成具体的值。 代码样例
应用场景比较不同长度的字符串字面常量。
函数模板定义了两个非类型模板参数参数N表示第一个数组的长度参数M表示第二个数组的长度。
数组采用const和引用的方式传参。
templateint N, int M
int compare(const char (p1)[N], const char (p2)[M])
{return strcmp(p1, p2);
}
非类型模板参数可以使用的数据类型
整型如int、long等
枚举类型
对象类型的引用或指针
函数的引用或指针
类成员的指针
当模板参数列表中同时有类型模板参数和非类型模板参数时建议将非类型模板参数写在类型模板参数的前面。
代码样例
template int lower, int upper, typename T
bool is_in_range(const T value)
{return (value upper) (value lower);
}
完整代码样例
求任意数据类型任意大小的数组的平均值。
#include iostream
template typename T, int N
T average(const T(array)[N])
{T sum{};int i;for (i 0; i N; i){sum array[i];}return sum / N;
}
int main()
{double array_1[2]{ 1.1, 2.1 };std::cout average(array_1) std::endl;float array_2[]{ 1.0, 2.0, 3.0, 4.0 };std::cout average(array_2) std::endl;int array_3[] { 1, 2, 3, 4 };std::cout average(array_3) std::endl;return 0;
}
运行结果
1.6
2.5
2
8.inline/constexpr修饰的函数模板
和具体函数一样函数模板可以用inline或constexpr修饰。
inline或constexpr在修饰时放在模板参数列表之后返回值类型之前。
代码样例
template typename T
inline T min(const T, const T);
9.函数模板的重载
函数模板的重载有两种方式
方式1.用同名函数重载函数模板
方式2.用另一个函数模板重载已有模板 重载的代码样例
template typename T
T larger(const T data[], size_t count)
{T result {data[0]};for (size_t i {1}; i count; i){if (data[i] result)result data[i];}return result;
}template typename T
T larger(const std::vectorT data)
{T result {data[0]};for (auto value : data){if (value result)result value;}return result;
}
二函数模板的特例
1.基础概念
函数模板的特例是由原始的函数模板具体化而来的因此函数模板的特例也被称为函数模板的具体化(explicit specialization)。
函数模板的特例的定义必须放在函数模板的声明和定义之后。
当编译器找到与函数调用匹配的具体化定义时将直接使用该函数模板的特例而不再实例化函数模板。
函数模板的特例也以关键字template开头但要省略参数所以template后面的尖括号是空的。
函数模板的特例的定义需要传递具体的参数类型。
当函数模板的某个实例需要被定义一种不同于原始函数模板的行为就可以使用函数模板的特例去定义。
空的尖括号“”表示编译器不需要做类型推导。 函数模板特例的简单样例
template
void func(int a, double b)
{//process code
}
2.代码样例
给定函数模板 larger(T1 a, T2 b)
template typename T1, typename T2
decltype(auto) larger(T1 a, T2 b)
{return a b ? a : b;
}
由于该函数模板不适用于指针数据类型因此定义以下函数模板的特例。
函数模板的特例在代码逻辑中相比原始的函数模板多了解引用操作。
template
int* largerint*(int* a, int* b)
{return *a *b ? a : b;//解引用操作是为了让两个指针比较指向的数值而不是地址
}
普通函数函数模板函数模板特例的代码形式
//function
void Swap(int a, int b);//template prototype
template typename T
void Swap(T a, T b);//template explicit specialization
template
void Swapint(int a, int b);
3.编译时的匹配优先级
当某个具体的数据类型可以同时匹配上普通函数函数模板函数模板的特例时普通函数的调用优先于函数模板特例函数模板特例的调用优先于原始函数模板。 三参考阅读
《C17入门经典》
《C primer》
《深入理解C11》
https://www.programiz.com/cpp-programming/function-template