CC++模板
# C/C++:模板
# 模板基础概念
C++提供两种模板机制:
- 函数模板
- 类模板
语法:
template<typename T>
函数/类声明或定义
typename可用class代替
T 通用数据类型,名称可替换,通常为大写字母
两种方式使用模板:
- 自动类型推导
- 显示指定类型
# 基础进阶:非类型模板参数推导
模板参数 typename T
中的T不仅仅可以是某种类型,还有一种常见模板参数形式可以让不同字面量成为模板参数,即非类型模板参数。
模板参数只支持整数类型(包括
enum
),浮点类型、指针类型、自定义类型等均不能声明为模板参数。
使用非类型模板参数与使用函数参数有何区别?前者是编译期常量,因而可以在编译期对代码进行整体优化。例如对函数进行输出调试:
使用非类型模板参数:
template <bool debug>
int add(int a, int b) {
// C++17之后:
// 为确保编译期确定分支,可以使用 if constexpr
if (debug) {
std::cout << a << '\t' << b << '\n';
}
return a + b;
}
int main() {
// std::cout << add<1>(3, 5) << '\n';
std::cout << add<0>(3, 5) << '\n';
return EXIT_SUCCESS;
}
对于开启和关闭调试的汇编结果如下两图,左图为开启调试模式,右图为关闭调试模式,可见当使用非类型模板参数时,编译期会优化掉输出的代码:
使用函数参数:
int add(int a, int b, bool debug)
{
if (debug) {
std::cout << a << '\t' << b << '\n';
}
return a + b;
}
int main() {
std::cout << add(3, 5, 0) << '\n';
return EXIT_SUCCESS;
}
关闭调试的汇编结果如下图,可见当使用函数参数时,即便关闭调试选项,结果中依然会存在多余的输出代码,综上,合理使用非类型模板参数可以有效提高程序执行效率。
# 函数模板
# 普通函数与函数模板
两者的区别:
- 普通函数调用可发生隐式类型转换
- 函数模板
- 用自动类型推导,不发生隐式类型转换
- 用显示指定类型,发生隐式类型转换
调用规则:
void myPrint(int a)
{cout << a <<endl;}
template<class T>
void myPrint(T a)
{cout << a <<endl;}
优先调用普通函数
// 调用 myPrint(5);
可通过空模板参数列表强制调用函数模板
// 强制调用模板 myPrint<>(5);
若函数模板可产生更好地匹配,优先调用函数模板
char c = 'a'; myPrint(c);
函数模板也可以发生重载
# 具体化模板(特化)
- 自定义类型传入时,需要利用具体化模板
class Person
{
Person(int a, string n):m_Age(a),m_Name(n){};
int m_Age;
string m_Name;
}
template<class T>
bool myCompare(T &a, T &b)
{
if(a==b) return true;
else return false;
}
// 具体化模板
template<> bool myCompare(Person &p1, Person &p2)
{
if (p1.m_Name==p2.m_Name && p1.m_Age==p2.m_Age)
return true;
else
return false;
}
# 类模板
template<class NameType, class AgeType>
class Person
{
public:
Person(NameType name, AgeType age):m_Name(name),m_Age(age){}
NameType m_Name;
AgeType m_Age;
}
类模板与函数模板的区别主要为:
- 类模板没有自动类型推导
- 类模板在模板参数列表中可以有默认参数
template<class NameType, class AgeType = int>
// class defination
# 类模板对象做函数参数
三种类模板对象做函数参数的方法:
- ※指定传入类型
- 参数模板化
- 整个类模板化
已知一个类模板如下:
template <typename K, typename W>
class MyDict
{
public:
MyDict() : m_key("null"), m_word(0) {}
MyDict(K key, W word) : m_key(key), m_word(word) {}
~MyDict() {}
private:
K m_key;
W m_word;
public:
void print()
{
std::cout << '{' << this->m_key << " , " << this->m_word << '}' << std::endl;
}
};
# 指定传入类型
// 直接指定传入类型
void PrintMyDict1(MyDict<int, int> &md)
{md.print();}
# 参数模板化
template<typename T1, typename T2>
void PrintMyDict2(MyDict<T1, T2> &md)
{
md.print();
}
可以通过
typeid(T1).name()
查看类型
由于 GCC 编译器的优化,无法通过上述表达式查看类型,可以加上
#include <cxxabi.h>
这个头文件,并使用abi::__cxa_demangle(typeid(int).name(),0,0,0 )
#include <cxxabi.h> template<typename T> void view_typeid_name() { std::cout << abi::__cxa_demangle(typeid(T).name(),0,0,0) << std::endl; }
# 整个类模板化
template<typename T1>
void PrintMyDict3(T1 &md)
{
md.print();
view_typeid_name<T1>();
}
# 类模板与继承
当类模板被继承时,需要注意:
- 子类在声明的时候,要指定出父类中T的类型,可将子类也变为类模板
template<typename T>
class Base
{
T m;
};
class Son:public Base<int>
{};
template<typename T1, typename T2>
class Son2:public Base<T1>
{
T2 obj;
};
# 类模板成员函数类外实现
// 构造函数类外实现
template<typename T1, typename T2>
Person<T1,T2>::Person(T1 name, T2 age)
{ /* do something */ }
// 成员函数类外实现
template<typename T1, typename T2>
void Person<T1, T2>::showPerson()
{ /* do something */ }
# 类模板分文件
由于类模板中的成员函数创建时机是在调用阶段,导致分文件编写时链接不到。
解决方案:
- 直接包含
.cpp
源文件 - 将声明和实现写到同一个文件中,并更改后缀名为
.hpp
# 类模板与友元
配合友元函数的类内和类外实现
- 类内实现 - 直接在类内声明友元即可
- 类外实现 - 提前让编译器知道全局函数的存在
// 告诉下方的函数模板printDict_out中的MyDict是一个模板类
template <typename K, typename W>
class MyDict;
// 让编译器提前知道该MyDict的友元模板函数的存在
template <typename K, typename W>
void printDict_out(MyDict<K, W> md)
{
std::cout << '{' << md.m_key << " , " << md.m_word << '}' << std::endl;
}
template <typename K, typename W>
class MyDict
{
// 友元全局函数 类内实现
friend void printDict_in(MyDict<K, W> md)
{
std::cout << '{' << md.m_key << " , " << md.m_word << '}' << std::endl;
}
// 友元全局函数 类外实现
// 加空模板参数列表,表示是函数模板的函数声明
friend void printDict_out<>(MyDict<K, W> md);
public:
MyDict() : m_key("null"), m_word(0) {}
MyDict(K key, W word) : m_key(key), m_word(word) {}
~MyDict() {}
private:
K m_key;
W m_word;
};
编辑 (opens new window)
上次更新: 2023/08/16, 16:01:49