C++ 模板2

文章目录

  • 一、非类型模板参数
  • 二、模板特化
  • 三、模板分离编译的问题

一、非类型模板参数

模板参数分为 类型形参非类型形参

  • 类型形参:用 class 和 typename 定义的参数
  • 非类型形参:模板参数中声明的一个变量,这个变量只能是整形家族的,并且只能当常量使用
#include 

using namespace std;

// T 为类型参数,N 为非类型参数,只可以当常量使用
//模板参数的缺省规则和函数参数的缺省规则一样
template<class T, size_t N = 10>
class Array
{
public:
	size_t capaciyt() const
	{
		return N;
	}

private:
	T arr[N];
};

int main()
{
	Array<int> a1;
	Array<int, 20> a2;

	// 输出 10 20
	cout << a1.capaciyt() << " " << a2.capaciyt() << endl;

	return 0;
}

二、模板特化

用模板可以实现通用的函数和类,但是有时想要对该模板中的某些类型进行特殊化处理,就需要使用模板特化,函数模板和类模板都可以进行特化,但通常函数模板特化可以用函数重载替代

模板特化的语法:

1. 必须要先有一个基础的函数模板或类模板
2. 模板关键字 template 后跟 <>,<> 中写出未特化的类型,都特化了就不写
   如果是偏特化中的满足条件特化则需要使用 typename 关键字后跟偏特化的类型
3. 函数名或类名后跟 <>,<> 中指定需要特化的类型,未特化的类型也需要写出
4. 函数名或类名、函数形参表需要和原模板保持一致

模板特化的使用:

#include 

using namespace std;

// 定义一个通用的小于的仿函数
template<class T>
struct Less
{
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

// 对 int* 进行特化
template<>
struct Less<int*>
{
	bool operator()(const int* x, const int* y)
	{
		return *x < *y;
	}
};

int main()
{
	// 创建匿名对象调用 less 模板,输出 1
	cout << Less<int>()(1, 2) << endl;

	// 如果还是调用 less 模板,便只能用地址比较,可能输出 0
	// 通过特化后,可以用指针指向的内容进行比较,输出 1
	int* p1 = new int(1);
	int* p2 = new int(2);
	cout << Less<int*>()(p1, p2) << endl;

	return 0;
}

模板特化分为 全特化偏特化(半特化)

  • 全特化:将模板参数列表中的所有参数都确定化
  • 偏特化:1. 对部分参数特化, 2. 对满足条件的类型特化
#include 

using namespace std;

// 类模板
template<class T1, class T2>
class demo
{
public:
	// 构造函数
	demo()
	{
		cout << "demo" << endl;
	}
};

// 全特化
template<>
class demo<int, char>
{
public:
	demo<int, char>()
	{
		cout << "demo" << endl;
	}
};

// 偏特化,对部分参数特化
template<class T1>
class demo<T1, double>
{
public:
	demo<T1, double>()
	{
		cout << "demo" << endl;
	}
};

// 偏特化,对满足条件的类型特化
// 当两个参数都是指针类型时的特化
template<typename T1, typename T2>
struct demo<T1*, T2*>
{
public:
	demo<T1*, T2*>()
	{
		cout << "demo" << endl;
	}
};

int main()
{
	// 调用模板,输出 demo
	demo<int, int> d1;

	// 调用全特化,输出 demo
	demo<int, char> d2;

	// 调用偏特化中的部分特化,输出 demo
	demo<int, double>d3;

	// 调用偏特化中的满足条件特化,输出 demo
	demo<int*, double*>d4;

	return 0;
}

三、模板分离编译的问题

通常在写项目时,都会将声明写在 .h 文件中,定义写在 .cpp 文件中,但是对于函数模板和类模板而言,如果将声明和定义分离,将会出现链接错误

// demo.h
#pragma once

#include 

using namespace std;

template<class T>
class demo
{
public:
	// 声明函数
	demo();
};

// demo.cpp
#include "demo.h"

// 定义函数
template<class T>
demo<T>::demo()
{
	cout << "demo()" << endl;
}

// test.cpp
#include "demo.h"

int main()
{
	demo<int> d;

	return 0;
}

编译时提示链接错误
C++ 模板2_第1张图片

  • 文件经过预处理后,.h 会被拷贝到包含的 .cpp 文件中
// demo.cpp
#pragma once

#include 

using namespace std;

template<class T>
class demo
{
public:
	// 声明函数
	demo();
};

// 定义函数
template<class T>
demo<T>::demo()
{
	cout << "demo()" << endl;
}

// test.cpp
#pragma once

#include 

using namespace std;

template<class T>
class demo
{
public:
	// 声明函数
	demo();
};

int main()
{
	demo<int> d;

	return 0;
}
  • 编译时 .cpp 文件都会单独编译

编译完成后,由于 test.cpp 文件中有 demo 的声明,因此 demo d 可以转化为 call demo()(地址),不过在 demo.cpp 文件中虽然有 demo 的声明和定义,但是 demo 无法知道要实例化什么类型的 demo,因此也就不会实例化,所以链接时,test.cpp 中就无法找到 demo,也就导致了链接错误

解决方法:

  • 显示实例化,这个方法不好用
// 在 demo.cpp 文件中,像如下声明需要使用的类型
template
demo<int>::demo();
  • 声明和定义都放在 .h 文件中或者 .hpp 这里的后缀是用来暗示这个文件是模板,推荐使用这种
// 只写 demo.h 文件,不需要 demo.cpp 文件
#pragma once

#include 

using namespace std;

template<class T>
class demo
{
public:
	// 声明函数
	demo();
};

// 定义函数
template<class T>
demo<T>::demo()
{
	cout << "demo()" << endl;
}

模板优点:模板增加了代码的复用性
模板缺点:模板出现编译错误时,错误信息非常凌乱,不容易查找到错误

你可能感兴趣的:(C++,c++,开发语言)