本专栏目的
- 更新C/C++的基础语法,包括C++的一些新特性
前言
- 模板与元编程是C++的重要特点,也是难点,本人预计将会更新10期左右进行讲解,这是第四期,有些和前面三期重合,这一期也是为明天更新打下基础;
- C语言后面也会继续更新知识点,如内联汇编;
- 欢迎收藏 + 关注,本人将会持续更新。
模板参数
前三掌已经讲解了函数、类、变量模板以及他们的特性等等,这一小节模板参数讲解,有一部分是对前面知识点的提取,👁👁👁👁👁,这一小节主要讲解一下几个部分:
- 类型模板参数
- 整数
- 指针
- 模板类型
💁♂ 注意:,模板这个东西比较新,本人是在C++20的标准下进行学习的。
类型模板参数
☄ 这个是最常用的,就是用typename
关键字定义的类型,如下:
template <typename T>
void show(T t)
{
std::cout << "t: " << t << std::endl;
}
⭕️ 其中T
可以是任意类型参数,如下显示:
show<int>(50);
show<void*>(nullptr);
show<std::string>("HelloWorld");
/*结果:
t: 50
t: 0000000000000000
t: HelloWorld
*/
其他,还可以结合**const、&**等结合,如下代码:
#include <iostream>
#include <type_traits>
template <typename T>
void show(T& t)
{
std::cout << "t: " << t << std::endl;
// 判断是否为 int 类型
if (std::is_same_v<T, int>) {
t = 100;
}
}
int main()
{
int a = 50;
show<int>(a);
std::cout << "a: " << a << std::endl;
return 0;
}
这段代码中,变量类型使用&
修饰,说明这个是引用传递,结果如下:
🚿 概括:进一步说明了,模板参数兼容任意类型。
整型
整型:比如说int
、short
、char
,bool
类型等,C++20后,可以支持double
。
这个部分前三章讲过,这里就不详细讲解了,总的来说就是,不用typename
声明类型,直接用int、short、size_t等
类型直接声明,如下:
template <size_t size>
void print()
{
std::cout << "size: " << size << std::endl;
}
int main()
{
print<100>(); // 直接打印
return 0;
}
⛑ 结果:
🚯🚯🚯🚯🚯 注意
模板是在编译的时候计算出来的,如果传递的是不是在编译的时候编译出来的东西,就会报错,如下代码:
template <size_t size>
void print()
{
std::cout << "size: " << size << std::endl;
}
int main()
{
std::vector<int> temp{ 1, 2, 3, 4, 5 ,6 ,7 };
print<temp.size()>();
return 0;
}
🚻 结果
原因:.size()
方法是在运行的时候确定的,不是在编译期间。
指针
变量指针这个当然可以,上面案例也有,这里讲解函数指针的情况,模板参数,还可以作为函数指针,代码如下:
template <typename T>
void add(T a, T b)
{
std::cout << "a + b: " << a + b << std::endl;
}
/// 函数指针作为参数
template <typename T, void (*add)(T a, T b)>
void add_num(T a, T b)
{
add(a, b);
}
int main()
{
add_num<int, add<int>>(20, 30);
return 0;
}
结果:
模板嵌套类型
关键点在于大家自己要清楚类型如何表示(类型是由类名<类型>表示一个类型)
来个案例:
#include <iostream>
template<typename T1, typename T2, typename T3>
class Data
{
public:
Data(T1 t1, T2 t2, T3 t3) : m_data1(t1), m_data2(t2), m_data3(t3) {}
void print()
{
std::cout << "m_data1: " << m_data1 << " m_data2: " << m_data2 << " m_data3: " << m_data3 << std::endl;
}
private:
T1 m_data1;
T2 m_data2;
T3 m_data3;
};
template<typename T1, typename T2>
class Info
{
public:
Info(T1 t1, T2 t2) : m_data1(t1), m_data2(t2) {}
void print()
{
std::cout << "data1: " << m_data1 << " data2: " << m_data2 << std::endl;
}
private:
T1 m_data1;
T2 m_data2;
};
int main()
{
Data<int, int, int> data(10, 20, 30);
data.print();
Info<int, int> info1(50, 60);
info1.print();
/*
m_data1: 10 m_data2: 20 m_data3: 30
data1: 50 data2: 60
*/
return 0;
}
💇 上面案例就是很简单的案例,定义了两个案例模板,后买你定义了一个打印数据的函数;
👔 现在需求:将模板类作为参数,代码如下:
Info<Data<int, int, int>, Data<std::string, std::string, std::string>> info2(Data<int, int, int>(66, 77, 88), Data<std::string, std::string, std::string>("w", "y", "z"));
info2.print();
这个时候运行:
报错了: 原因原容易找到,这个在之前的运算符重载又讲(C/C++语言基础–C++运算符重载以及其重载限制-CSDN博客),只需要加个运算符重载即可。
template<typename T1, typename T2, typename T3>
class Data
{
public:
Data(T1 t1, T2 t2, T3 t3) : m_data1(t1), m_data2(t2), m_data3(t3) {}
void print()
{
std::cout << "m_data1: " << m_data1 << " m_data2: " << m_data2 << " m_data3: " << m_data3 << std::endl;
}
// 运算符从在即可
friend std::ostream& operator<<(std::ostream& out, const Data& data) {
out << data.m_data1 << " " << data.m_data2 << " " << data.m_data3 << "\n";
return out;
}
private:
T1 m_data1;
T2 m_data2;
T3 m_data3;
};
结果:
data1: 66 77 88
data2: w y z
核心: 要注意数据如何表示,输出的时候如何输出,即可;