IE盒子

搜索
查看: 124|回复: 0

c++模板知识点整理1--模板基础

[复制链接]

3

主题

8

帖子

15

积分

新手上路

Rank: 1

积分
15
发表于 2023-4-10 19:26:02 | 显示全部楼层 |阅读模式
一、函数模板

  • 类型推断中的类型转换
函数模板的类型推断可以让我们像调用普通函数一样调用模板函数,而不用显示的指明模板参数的类型。
1.1 如果调用参数是按引用传递的,任何类型转换都不被允许。
1.2 如果调用参数是按值传递的,那么只有decay是被允许的。
所谓的decay是指:const 和volatile限制符会被忽略,引用会被忽略,数组会被转化为指针。
1.3 不能对默认参数进行类型推断
例:
template <typename T>
void f( T a = 1 )
{

}
f();// Error : 无法推断T的类型
1.4 模板函数的返回类型推断
1.4.1 c++11的返回类型推断方法--尾置返回类型(Trailing Return Type)
例:
template<typename T1, typename T2>
auto add( T1 a, T2 b ) -> decltype( a + b )
{
  return a + b;
}注意这里使用了decltype这个关键词,它的作用是选择并返回操作数的类型。
1.4.2 c++14以后
例:
template<typename T1, typename T2>
auto add( T1 a, T2 b )
{
  return a + b;
}在c++14之后可以取消尾置返回类型而由auto自动推导,但是这里有一个前提:返回类型必须能通过函数体中的返回语句推断出来。也就是说必须要有这样可以用来推断返回类型的返回语句,而且多个返回语句之间的推断结果必须一致。
1.4.3 使用std::common_type_t来推断返回类型
template<typename T1, typename T2>
std::common_type_t<T1, T2> add( T1 a, T2 b )
{
  return a + b;
}std::common_type_t<>可以产生两个模板参数中的公共类型。
二、类模板

  • 模板特化
类模板的特化允许我们对某一特定类型做优化,或者去修正类模板针对某一特定类型实例化之后的行为。
例:
template<typename T>
class Base
{
......
};

template<>
class Base<int> // Base类针对int类型的特化
{
......
};

int main()
{
  Base<int> b; // 在实例化的时候会选择特化版本而不是原始版本的模板类。
}2. 偏特化
2.1 类型偏特化
例:
template<typename T>
class Base
{
......
};

template<typename T> // Base类针对T的指针类型的偏特化
class Base<T*>
{
......
};2.2 参数个数偏特化
template<typename T1, typename T2>
class Base
{
......
};

template<typename T2> // Base类针对多个模板参数中的其中一个或多个的类型偏特化
class Base<int, T2>
{
......
};2.3 默认模板参数
这个比较简单,例:
template<typename T = int>
class Base
{
......
};

itn main()
{
  Base a; // 没有显示指明模板类的模板参数时,会调用默认的模板参数(如果有的话)
}2.4 类型别名
2.4.1 使用typedef 定义类型别名
2.4.2 使用using关键词定义类型别名
例:
template<typename T>
class Base
{
.......
};

using BaseInt = Base<int>; // 使用using关键词定义别名
typedef Base<int> BaseInt; //  使用typedef 定义别名由于using关键词是c++11的特性,且更符合习惯,因此推荐使用这个。
2.5 别名模板(模板别名,template Alias/ Alias Template)
template<typename T1, typename T2>
class Base
{
......
};

template<typename T>
using Base1 = Base<int, T>; // Base1 是一个新的别名了,但是指定了其中一个参数类型三、模板非类型参数
对于函数模板以及类模板,都可以为其定义非类型的模板参数。目前c++11、c++14以及c++17支持的非类型参数主要有是整形常量(包含枚举),指向 objects/functions/members 的指针,objects 或者 functions 的左值引用,或者是 std::nullptr_t (类型是 nullptr)。
在c++17中,甚至可以用auto来指定模板的非类型参数,而使其可以用于任意有效的非类型模板参数的类型。
例:
template<auto T> // 这里T可以接收上述所说的任何一个支持的非类型参数
class Base
{
......
};四、可变参数模板
这里要介绍一下模板的第一个黑魔法:Variadic Template;可以将模板参数定义成能够接受任意多个模板参数的形式。
这里先列一个最为常见的例子:
void print()
{
  std::cout<<std::endl;
}

template<typename T, typename... Types>
void print( T arg, Types... args )
{
  std::cout<<arg<<", ";
  print( args... );
}

int main()
{
  print("hello", 3, 3.4);
}结果会打印:
hello, 3, 3.4这里的typename... Types中的Types叫做模板参数包(template parameter pack);
这里的Types... args中的args叫做函数参数包(function parameter pack);
由上面的例子可以在直觉上感觉到在调用print这个函数的过程中存在着一种“拆包”的过程。实际上确实如此,下面将简单介绍两种模板参数包解析的方法:

  • 递归解析
如上面的例子所示,我们定义了一个非模板且形参为空的print函数作为递归结束的条件。
在执行print("hello", 3, 3.4);这句话的时候,传进去的有三个东西:"hello", 3, 3.4。
第一次递归:
arg对应"hello",参数包args里还有:3,3.4;使用cout打印出arg也就是“hello”;
第二次递归:
arg对应3,参数包args里还有3.4;使用cout打印出3;
第三次递归:
arg对应3.4,参数包args为空;使用cout打印出3.4;由于参数包已经空了,将调用递归结束的print()函数打印出换行,结束;
2. c++17 折叠表达式
从 C++17 开始,提供了一种可以用来计算参数包(可以有初始值)中所有参数运算结果的二元运算符。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表