C++ TGP 模板基础知识--函数模板
Posted 血_影
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ TGP 模板基础知识--函数模板相关的知识,希望对你有一定的参考价值。
基本范例
- 模板的定义是以template关键字开头
- 类型模板参数T前面用typename来修饰,所以遇到typename就知道其后面跟的是一个类型,typename可以用class取代
- 类型模板参数T代表是一个类型,以及前面的修饰符typename/class都用<>括起来
- T这个名字可以换成任意其他标识符,对程序没有影响,用T只是一种编程习惯
实例化
编译时用具体的类型代替类型模板参数的过程叫做实例化,通过函数模板实例化之后的函数包含三部分:
- 模板名
- 后面跟着一对<>
- <>中间是一个具体类型
模板参数推断
常规参数推断
通过<>可以只指定一部分模板参数的类型,另一部分模板参数的类型可以通过调用时给的实参来推断
- auto 代替函数模板返回类
template <typename T, typename U> auto my_sub(T tv1, U tv2) return tv1 - tv2;
- decltype可以与auto结合使用来构成返回类型后置语法,这种后置语法也就是使用auto和decltype结合来完成返回类型的推导
template <typename T, typename U> auto my_sub(T tv1, U tv2) -> decltype(tv1 - tv2) return tv1 - tv2;
各种推断的比较以及空模板参数列表的推断
- 自动推断
- 指定类型模板参数
- 指定空模板参数列表,<>作用就是明确告知调用的时函数模板而不是普通函数
#include <iostream> #include <string> #include <vector> /*函数模板*/ template <typename T> T my_double(T value) return value * 2; int main(int argc, char** argv) auto res1 = my_double(3); // 自动类型推断,T=int auto res2 = my_double<int>(16.9); // 指定类型模板参数,T=int auto res3 = my_double<>(16.9); // 当即存在函数模板又存在普通函数时,<>指定调用函数模板而不是普通函数 std::cout << res1 << " " << res2 << " " << res3 << std::endl; return 0;
重载
函数(函数模板)名字相同,但是参数数量或参数类型不同。 函数模板和函数可以同时存在,此时可以把函数看成时一种重载,当普通函数和函数模板都比较合适时,编译器会优先选择普通函数。
#include <iostream>
#include <string>
#include <vector>
namespace demo
/*函数模板*/
template <typename T>
void func(T value)
std::cout << "func(T value) " << typeid(value).name() << " value:" << value << std::endl;
/*函数模板*/
template <typename T>
void func(T* value)
std::cout << "func(T* value) " << typeid(value).name() << " value:" << *value << std::endl;
/*普通函数*/
void func(int value)
std::cout << "func(int value) " << typeid(value).name() << " value:" << value << std::endl;
// namespace demo
int main(int argc, char** argv)
demo::func(10);
demo::func(20.0);
char a = 'a';
demo::func(a);
demo::func(&a);
std::string str = "string";
demo::func(str);
demo::func(&str);
return 0;
特化
全特化
- 泛化:大众化的、常规的,常规情况下写的函数模板都是泛化的函数模板
- 特化:代表着从泛化版本抽出来的一组子集
- 全特化:把泛化版本中的所有模板参数都用具体的类型来代替构成的一个特殊的版本(全特化版本)
函数调用优先级: 普通函数 > 全特化 > 特化 > 泛化void func<char, std::string>(char& t, std::string& u) // 全特化 void func(char& t, std::string& u) // 重载函数
偏特化(局部特化)
- 模板参数数量上的偏特化:函数模板不支持模板参数数量上的偏特化
- 模板参数范围上的偏特化: int->const int,类型变小:T->T*,T->T&, T-> T&&
- 通过重载实现模板参数数量上的偏特化
#include <iostream> #include <string> #include <vector> namespace demo /*函数模板*/ /* 泛化 */ template <typename T, typename U> void func(T& t, U& u) std::cout << "泛化 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl; /* 全特化 */ template <> void func<char, std::string>(char& t, std::string& u) std::cout << "全特化 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl; /* 偏特化 */ template <typename T, typename U> void func(const T& t, const U& u) std::cout << "偏特化 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl; /* 重载 */ template <typename U> void func(char& t, U& u) std::cout << "重载 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl; // namespace demo int main(int argc, char** argv) char a = 'a'; std::string str = "string"; demo::func(a, str); const char b = 'a'; const std::string str1 = "string"; demo::func(b, str1); return 0;
缺失参数
#include <iostream>
#include <string>
#include <vector>
namespace demo
/* 普通函数 */
int func(int val1, int val2)
return val1 + val2;
using FuncType = int (*)(int, int); // 函数指针
template <typename T, typename F = FuncType>
void myfunc(T val1, T val2, F pfunc = func)
std::cout << pfunc(val1, val2) << std::endl;
template <typename T = int, typename U>
void myfunc2(U u)
T t = u;
std::cout << "type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << u << std::endl;
// namespace demo
int main(int argc, char** argv)
demo::myfunc(10, 20);
demo::myfunc2(10.23);
return 0;
非类型模板参数
基本概念
前面的函数缪版涉及到的模板参数都是类型模板参数,需要用typename/class来修饰。模板参数还可以时非类型模板参数(普通的参数),非类型模板参数一般都是常量(编译器需要在编译时确定非类型模板参数的值)。不是任何类型的参数都可以作为非类型模板参数,一般允许做非类型模板参数类型如下所示:
- 整型或枚举、指针类型、左值引用类型、auto或者decltype(auto),…
#include <iostream>
#include <string>
#include <vector>
namespace demo
// T,U:类型模板参数
// val:非类型模板参数
template <typename T, typename U, int val = 100>
auto my_add(T t, U u)
return val + t + u;
// namespace demo
int main(int argc, char** argv)
auto res1 = demo::my_add<float, double>(10.2f, 20.1f);
auto res2 = demo::my_add<float, double, 200>(10.2f, 20.1f);
std::cout << "res1:" << res1 << " res2:" << res2 << std::endl;
return 0;
// res1:130.3 res2:230.3
比较奇怪的语法
- 不管是类型还是非类型模板参数,如果代码中没有使用则参数名可以省略
namespace demo // template <typename T, int val = 100> template <typename, int> // 代码中未使用T和val,省略 auto my_add() return 100; // namespace demo int main(int argc, char** argv) auto res1 = demo::my_add<double, 100>(); std::cout << "res1:" << res1 << std::endl; return 0;
- 类型前面可以增加一个typename修饰,明确标识一个类型
// 2个typename意义不一样 // typename T:typename 表示后面是一个类型模板参数,可以用class替换 // typename int val = 100:typename 标识后面修饰的是一种类型 template <typename T, typename int val = 100> auto my_add() return 100;
以上是关于C++ TGP 模板基础知识--函数模板的主要内容,如果未能解决你的问题,请参考以下文章