《C++新经典》第15章 模板与泛型

130 篇文章 4 订阅
92 篇文章 19 订阅

15.1 模板概念与函数模板的定义、调用

15.1.1 模板概念

vector是类模板,通过尖括号<>传递类型参数int,生成真正的类vector<int>
模板分为函数模板和类模板。

  • 泛型编程独立于任何特定类型的方式编写代码。
  • 模板是泛型编程的基础,是创建类或者函数的蓝图或者公式。
  • 模板机制允许定义类、函数时将类型作为参数。

15.1.2 函数模板的定义

int funcadd(int i1, int i2) {
	int addhe = i1 + i2;
	return addhe;
}
float funcadd(float i1, float i2) {
	float addhe = i1 + i2;
	return addhe;
}

template <typename T> //函数模板,typename或class,类型参数T
T funcadd(T i1, T i2) {
	T addhe = i1 + i2;
	return addhe;
}

15.1.3 函数模板的调用

无法根据提供的实参推断出模板参数时,需要用<>主动提供模板参数。
编译器推断出模板参数类型后,会实例化特定版本的参数。

int he = funcadd(3, 1);
//实例化如下函数:
int funcadd(int i1, int i2) {
	int addhe = i1 + i2;
	return addhe;
}

float he = funcadd(3.0f, 1.0f);
//实例化如下函数:
float funcadd(float i1, float i2) {
	float addhe = i1 + i2;
	return addhe;
}


float he = funcadd(3, 1.0f); //编译出错

15.1.4 非类型模板参数

模板参数列表里可以定义非类型参数(表示的是值)。
非类型模板参数值由用户提供或者编译器推断,必须是常量表达式,因为编译器编译时实例化模板(只有常量表达式才能在编译时确定值)。

template <int a, int b>
int funcaddv2() {
	int addhe = a + b;
	return addhe;
}

int result = funcaddv2<12, 13>();//显示指定模板参数,<>中提供额外信息

int a = 12;
int result = funcaddv2<a, 13>();//错误,a非常量表达式

template <typename T, int a, int b>
int funcaddv3(T c) {
	int addhe = (int)c + a + b;
	return addhe;
}

int result = funcaddv3<double, 12, 13>(11);//<>内double与()传递进去的11类型不一致,以<>内传递进去double类型为准,不是以11推断出来的int类型为准。
template<unsigned L1, unsigned L2>
int charscomp(char const (&p1)[L1], char const (&p1)[L2]) {
	return strcmp(p1, p2);
}

int result = charscomp("test2", "test");
//实例化版本
//charscomp(char const (&p1)[6], char const (&p1)[5])
#include <iostream>
#include <cstring>
using namespace std;

template<unsigned L1, unsigned L2>
//inline版本
inline int charscomp(char const (&p1)[L1], char const (&p2)[L2]) {
	cout <<L1 <<endl;
	cout <<L2 <<endl;
	return strcmp(p1, p2);
}

int main() {
	int result = charscomp("test2", "test");
	cout <<result <<endl;
	return 0;
}

函数模板定义通常放在h头文件中,函数模板定义不会生成相关代码,只有调用函数模板时,编译器才会实例化特定版本函数并生成函数相关代码。

15.2 类模板概念与类模板的定义、使用

15.2.1 类模板概念

实例化特定的类,编译器不能为类模板推断模板参数,模板名后面必须使用"<>"提供模板参数。
类模板可以避免重复代码,通过传递类型或非类型参数,应付不同数据类型,精简和通用。

15.2.2 类模板的定义

template<typename 形参名1, typename 形参名2, ..., typename 形参名n>
class 类名 {
	//...
};

类模板的声明和实现等内容,都写到.h文件中去。

myvector.h

#ifndef __MYVECTOR__
#define __MYVECTOR__

template<typename T>
class myvector {
public:
	typedef T* myiterator;
	//myvector(){};
	myvector& operator=(const myvector&);
	//或者myvector<T>& operator=(const myvector<>&);
	myiterator mybegin(){return &data;};
	myiterator myend();
private:
	T data;
};

#endif
myvector<int> tmpvec;
myvector<double> tmpvec2;
myvector<string> tmpvec3;

15.2.3 类模板的成员函数

类模板定义中实现的成员函数

template<typename T>
class myvector {
public:
	myvector(){};
	void myfunc(){};
};

类模板定义外实现的成员函数

template<typename T>
class myvector {
public:
	myvector();
	void myfunc();
};

template<typename T>
myvector<T>::myvector(){}

template<typename T>
void myvector<T>::myfunc(){}

实例化的模板,它的成员只有在使用的时候(调用)才会被实例化。

15.2.4 类模板名字的使用

类模板内部,可以直接使用类模板名,后面可以不加模板参数。

template<typename T>
class myvector {
public:
	typedef T* myiterator;
	myvector& operator=(const myvector&);
	//或者myvector<T>& operator=(const myvector<>&);
};

类模板之外实现。

template<typename T>
class myvector {
public:
	typedef T* myiterator;
	myvector& operator=(const myvector&);
};


template<typename T>
//第一个T表示实例化了的myvector
//第三个T不是必加
myvector<T>& myvector<T>::operator=(const myvector<T>&) {
	return *this;
}

15.2.5 非类型模板参数的使用

template<typename T, int size = 10>
class myarray {
public:
	void myfunc();
private:
	T arr[size];
};

template<typename T, int size>
void myarray<T, size>::myfunc() {
	cout <<size <<endl;
}

myarray<int> tmparr;
tmparr.myfunc();

myarray<int, 50> tmparr2;
tmparr2.myfunc();
//浮点型一般不能作为非类型模板参数
template<typename T, double size>
class myarray{};

//类类型不能作为非类型模板参数
class a{};
template<typename T, a size>
class myarray{};

15.3 使用typename的场合、函数模板、默认模板参数与趣味写法

15.3.1 typename的使用场合

(1)模板定义中表明typename后面的模板参数是类型参数。

template <typename T, int a, int b>
int func(T c){...}

template<typename T>
class myvector{...};

(2)typename用于标明类型(类型成员):

//typename标明myvector<T>::myiterator表示类型,而不是静态成员变量。
template<typename T>
typename myvector<T>::myiterator myvector<T>::mybegin() {
	//...
}

template<typename T>
typename T::size_type getlength(const T& c) {
	if(c.empty())
		return 0;
	return c.size();
}

string mytest = "love";
//size_type类型unsigned int,定义(typedef)在string类中。
string::size_type size = mytest.size();
string::size_type size2 = getlength(mytest);
cout <<size2 <<endl;

15.3.2 函数指针作为其他函数的参数

int mf(int tmp1, int tmp2) {
	return tmp1+tmp2;
}

typedef int(*FunType)(int,int);
void testfunc(int i, int j, FunType funcpoint) {
	cout <<funcpoint(i, j) <<endl;
}

testfunc(3, 4, mf);

15.3.3 函数模板趣味用法

int mf(int tmp1, int tmp2) {
	return tmp1+tmp2;
}

typedef int(*FunType)(int,int);

template<typename T, typename F>
void testfunc(const T& i, const T& j, F funcpoint) {
	cout <<funcpoint(i, j) <<endl;
}

//F为函数指针
testfunc(3, 4, mf);

class tc {
public:
	tc() {cout <<"构造函数" <<endl;}
	tc(const tc& t) {cout <<"拷贝构造函数" <<endl;}
	//重载()
	int operator()(int v1, int v2) const {
		return v1+v2;
	}
};

tc tcobj;

//调用拷贝构造函数
//F为tc
testfunc(3, 4, tcobj);

//调用构造函数,生成临时对象
testfunc(3, 4, tc());

若类重载了"()“运算符,该类实例可调用"对象名(参数)”,看起来像函数调用一样,那么该类生成的对象就是一种可调用对象。

15.3.4 默认模板参数

模板参数可以有默认值。

  1. 类模板
    类模板名后"<>“必须有,函数模板多数时候不需要”<>"(编译器推断类型)。
template<typename T = string, int size = 5>
class myarray{...};

myarray<> abc;//<>不能省略,类模板且使用默认类型值
myarray<int> def;
  1. 函数模板
    (1)必须同时为模板参数和函数参数指定默认值。
template<typename T, typename F = tc>
void testfunc(const T& i, const T& j, F funcpoint = F()) {
	cout <<funcpoint(i, j) <<endl;
}

(2)产生临时对象作为默认值。

void testfunc(const T& i, const T& j, F funcpoint = F()) {
	cout <<funcpoint(i, j) <<endl;
}
//等价于
void testfunc(const T& i, const T& j, tc funcpoint = tc()) {
	cout <<funcpoint(i, j) <<endl;
}

(3)tc类必须重载"()"运算符,可调用对象。

//默认类参数
class tc {
public:
	tc() {cout <<"构造函数" <<endl;}
	tc(const tc& t) {cout <<"拷贝构造函数" <<endl;}
	//重载()
	int operator()(int v1, int v2) const {
		return v1+v2;
	}
};
template<typename T, typename F = tc>
void testfunc(const T& i, const T& j, F funcpoint = F()) {
	cout <<funcpoint(i, j) <<endl;
}

//默认函数指针参数
typedef int(*FunType)(int, int);
int mf(int tmp1, int tmp2) {
	return tmp1+tmp2;
}
template<typename T, typename F = FunType>
void testfunc(const T& i, const T& j, F funcpoint = mf) {
	cout <<funcpoint(i, j) <<endl;
}

testfunc(3, 4);

15.4 成员函数模板,模板显示实例化与声明

15.4.1 普通类的成员函数模板

普通类的成员函数(非虚函数)本身是函数模板。

class A {
public:
	template<typename T>
	void myft(T tmpt) {
		cout <<tmpt <<endl;
	}
};

A a;
a.myft(3);

15.4.2 类模板的成员函数模板

类模板和其成员函数模板有各自独立的模板参数。

template<typename C>
class A {
public:
	template<typename T2>
	A(T2 v1, T2 v2) {}

	template<typename T>
	void myft(T tmpt) {
		cout <<tmpt <<endl;
	}
	
	C m_ic;
};

//成员函数实现代码写到类模板定义之外
//类模块成员函数定义
template<typename T2>
A(T2 v1, T2 v2);

//类模块成员函数实现
template<typename C>
template<typename T2>
A<C>::A(T2 v1, T2 v2) {}
	
A<float> a(1, 2);
A<float> a2(1.1, 2.2);
a.myft(3);

15.4.3 模板显示实例化与声明

模板只有被使用时才会实例化。

//ca.h
#ifndef CA_H
#define CA_H

#include <iostream>
using namespace std;

template<typename C>
class A {
public:
	template<typename T2>
	A(T2 v1, T2 v2);

	template<typename T>
	void myft(T tmpt) {
		cout <<tmpt <<endl;
	}
	
	C m_ic;
};

template<typename C>
template<typename T2>
A<C>::A(T2 v1, T2 v2) {
	cout <<v1 <<v2 <<endl;
}

#endif

//test.cpp
#include "ca.h"
void mfunc() {
	A<float> a(1, 2);
}

//main.cpp
#include "ca.h"
int main() {
	A<float> a(1, 2);
	A<float> a2(1.1, 2.2);
	a.myft(3);
	return 0;
}
//test.cpp和main.cpp都会实例化A<float>类。

显式实例化避免生成多个相同类模板实例的开销。

//a.h
#ifndef A_H
#define A_H

#include <iostream>
using namespace std;

template<typename C>
class A {
public:
	template<typename T2>
	A(T2 v1, T2 v2);

	template<typename T>
	void myft(T tmpt) {
		cout <<tmpt <<endl;
	}
	
	C m_ic;
};
extern template A<float>;

template<typename T>
void myfunc(T v1, T v2);
extern template void myfunc(int &v1, int& v2);

#endif


//a.cpp
#include "a.h"

template<typename C>
template<typename T2>
A<C>::A(T2 v1, T2 v2) {
	cout <<v1 <<v2 <<endl;
}
template A<float>;//会将类模板及所有成员函数都实例化出来,包括内联成员函数。

template<typename T>
void myfunc(T v1, T v2) {
	cout <<v1+v2 <<endl;
}
template void myfunc(int &v1, int& v2);

15.5 using定义模板别名与显示指定模板参数

15.5.1 using定义模板别名

//typedef给固定类型起别名
typedef std::map<std::string, int> map_s_i;
map_s_i mymap;
mymap.insert({"first", 1});

typedef std::map<std::string, std::string> map_s_s;
map_s_s mymap2;
mymap2.insert({"first", "second"});

//c++98通过类模板实现类型名不固定
template<typename T>
struct map_s {
	typedef std::map<std::string, T> type;
};
map_s<int>::type map;
map.insert({"first", 1});

//c++11
template<typename T>
using map_s = std::map<std::string, T>;
map_s<int> map;
map.insert({"first", 1});

//using用来给类型有关模板起别名
//using包含了typedef的所有功能
typedef unsigned int uint_t;
using uint_t = unsigned int;

typedef std::map<std::string, int> map_s_i;
using map_s_i = std::map<std::string, int>;

typedef int(*FunType)(int,int);
using FunType = int(*)(int,int);


int RealFunc(int i, int j) {return 3;}
template<typename T>
using myfunc_M = int(*)(T,T);
myfunc_M<int> pointFunc;//类型名,非类模板实例化后的类
pointFunc = RealFunc;
cout <<pointFunc(1, 6) <<endl;

15.5.2 显示指定模板参数

template<typename T1, typename T2, typename T3>
T1 sum(T2 i, T3 j) {
	T1 result = i + j;
	return result;
}

auto result = sum(2000000000, 2000000000);//错误,未指定T1

//结果错误,指定T1为int,T2和T3推断为int
//2000000000+2000000000超出int范围
auto result = sum<int>(2000000000, 2000000000);
auto result = sum<double>(2000000000, 2000000000);

//类型不对,编译器报错
auto result = sum<int, string, string>(2000000000, 2000000000);

//正确
auto result = sum<double, double, double>(2000000000, 2000000000);
auto result = sum<double, double>(2000000000, 2000000000);


template<typename T1, typename T2, typename T3>
T3 sum(T1 i, T2 j) {
	T3 result = i + j;
	return result;
}
//格式不正确
auto result = sum<, , double>(12, 13);

15.6 模板全特化与偏特化(局部特化)

泛化(更宽广的范围),模板就是一种泛化表现,可以指定任意的模板参数。
特化,类型特殊对待,编写专门的代码。

15.6.1 类模板特化

  1. 类模板全特化

1)常规全特化
泛化的类模板

template<typename T, typename U>
struct TC {
	TC(){
		cout<<"TC()"<<endl;
	}
	void functest(){
		cout<<"TC::functest()"<<endl;
	}
};

全特化,就是所有类型模板参数(T和U)都用具体类型代替。

template<>
struct TC<int, int> {
	TC(){
		cout<<"TC<int, int>()"<<endl;
	}
	void functest(){
		cout<<"TC<int, int>::functest()"<<endl;
	}
};

template<>
struct TC<double, int> {
	TC(){
		cout<<"TC<double, int>()"<<endl;
	}
	void functest(){
		cout<<"TC<double, int>::functest()"<<endl;
	}
};
TC<char, int> tcchar;
tcchar.functest();

TC<int, int> tcint;
tcint.functest();

TC<double, int> tcdouble;
tcdouble.functest();

2)特化类模板的成员函数

template<>
void TC<double, double>::functest(){
	cout<<"TC<double, double>::functest()"<<endl;
}
  1. 类模板偏特化(局部特化)

1)模板参数数量上的偏特化

template <typename T, typename U, typename W>
struct TCP {
	TCP() {
		cout<<"TCP()"<<endl;
	}
	void functest(){
		cout<<"TCP::functest()"<<endl;
	}
};
template <typename U>
struct TCP<int, U, double> {
	TCP() {
		cout<<"TCP<int, U, double>()"<<endl;
	}
	void functest(){
		cout<<"TCP<int, U, double>::functest()"<<endl;
	}
};
TCP<double, int, double> tcpdi;
tcpdi.functest();

TCP<int, int, double> tcpii;
tcpii.functest();

2)模板参数范围上的偏特化

template <typename T>
struct TCF {
	TCF() {
		cout<<"TCF()"<<endl;
	}
	void functest(){
		cout<<"TCF::functest()"<<endl;
	}
};
template <typename T>
struct TCF<const T> {
	TCF() {
		cout<<"TCF<const T>()"<<endl;
	}
	void functest(){
		cout<<"TCF<const T>::functest()"<<endl;
	}
};

template <typename T>
struct TCF<const T *> {
	TCF() {
		cout<<"TCF<const T *>()"<<endl;
	}
	void functest(){
		cout<<"TCF<const T *>::functest()"<<endl;
	}
};

template <typename T>
struct TCF<const T&> {
	TCF() {
		cout<<"TCF<const T&>()"<<endl;
	}
	void functest(){
		cout<<"TCF<const T&>::functest()"<<endl;
	}
};

template <typename T>
struct TCF<const T&&> {
	TCF() {
		cout<<"TCF<const T&&>()"<<endl;
	}
	void functest(){
		cout<<"TCF<const T&&>::functest()"<<endl;
	}
};
TCF<double> td;
td.functest();

TCF<double*> tcfd;
tcfd.functest();

TCF<const int> tcfi;
tcfi.functest();

TCF<int&> tcfyi;
tcfyi.functest();

TCF<int&&> tcfyii;
tcfyii.functest();

15.6.2 函数模板特化

  1. 函数模板全特化
template <typename T, typename U>
void tfunc(T& t1, T& t2) {
	cout<<"tfunc()"<<endl;
	cout<<t1<<endl;
	cout<<t2<<endl;
}
template <>
void tfunc(int& t1, double& t2) {
	cout<<"tfunc<int, double>()"<<endl;
	cout<<t1<<endl;
	cout<<t2<<endl;
}
//函数重载
//优先普通函数
//其次函数模板的特化版本(最最合适的版本)
//最后函数模板的泛化版本
void tfunc(int& t1, double& t2) {
	cout<<"tfunc<int, double>()"<<endl;
	cout<<t1<<endl;
	cout<<t2<<endl;
}
int k=12;
double db=15.8f;
tfunc(k, db);
  1. 函数模板偏特化

函数模板不能偏特化.

//错误
template <typename U>
void tfunc<double, U>(double& t1, U& t2) {
}

15.6.3 模板特化版本放置位置

.h文件中,特化版本放在泛化版本后面。

15.7 可变参模板与模板模板参数

Variadic Templates,0到任意个模板参数,…

15.7.1 可变参函数模板

template<typename... T>
void func(T... args){
	cout<<sizeof...(args)<<endl;
	cout<<sizeof...(T)<<endl;//同上
}

func();
func(10, 2.3, "abc");

args称为一包或者一堆参数,0到多个不同类型.

template<typename T, typename... U>
void func2(const T& firstarg, const U&... otherargs){
	//cout<<sizeof...(firstarg)<<endl;//error
	cout<<sizeof...(otherargs)<<endl;
}

//func2();//error
func2(10);
func2(10, 2.3, "abc");
  1. 参数包的展开
void func3() {
	cout<<"Over!"<<endl;
}

template<typename T, typename... U>
void func3(const T& firstarg, const U&... otherargs){
	cout<<firstarg<<endl;
	cout<<func3(otherargs...)<<endl;
}

func2(10, 2.3, "abc");

15.7.2 可变参类模板

  1. 通过递归继承方式展开参数包
template<typename... Args>
class myclasst{
public:
	myclasst(){
		printf("myclasst::myclasst(), this = %p\n", this);
	}
};

template<typename First, typename... Others>
classt<First, Others...>:private myclasst<Others...>{
public:
	myclasst():m_i(0){
		printf("myclasst::myclasst(), this = %p, sizeof...(Others) = %d\n", this, sizeof...(Others));
	}
	First m_i;
};

myclasst<int, float, double> myc;
template<>
class myclasst<>{
public:
	myclasst(){
		printf("myclasst<>::myclasst(), this = %p\n", this);
	}
};

template<typename First, typename... Others>
classt<First, Others...>:private myclasst<Others...>{
public:
	myclasst():m_i(0){
		printf("myclasst::myclasst(), this = %p, sizeof...(Others) = %d\n", this, sizeof...(Others));
	}
	myclasst(First parf, Others... paro):m_i(parf),myclasst<Others...>(paro...){
		cout<<"begin"<<endl;
		printf("myclasst::myclasst(parf, ...paro), this = %p, sizeof...(Others) = %d\n", this, sizeof...(Others));
		cout<<"m_i="<<m_i<<endl;
		cout<<"end"<<endl;
	}
	First m_i;
};

myclasst<int, float, double> myc(12, 13.5, 23);
  1. 通过递归组合方式展开参数包
template<typename First, typename... Others>
class myclasst<First, Others...>{
public:
	myclasst():m_i(0){
		printf("myclasst::myclasst(), this = %p\n", this);
	}
	myclasst(First parf, Others... paro):m_i(parf),m_o(paro...){
		cout<<"begin"<<endl;
		printf("myclasst::myclasst(parf, ...paro), this = %p, sizeof...(Others) = %d\n", this, sizeof...(Others));
		cout<<"m_i="<<m_i<<endl;
		cout<<"end"<<endl;
	}
	First m_i;
	myclasst<Others...> m_o;
};

myclasst<int, float, double> myc(12, 13.5, 23);
  1. 通过tuple和递归调用展开参数包

tuple,元组,可以装各种不同类型的元素(数据)。

template<typename...T>
void functuple(const tuple<T...> &t){}


tuple<float, int, int> mytuple(12.5f, 100, 52);
cout<<get<0>(mytuple)<<endl;

functuple(mytuple);
template<int mycount, int mymaxcount, typename ...T>
class myclasst2{
public:
	static void mysfunc(const tuple<T...> &t){
		cout<<"value = "<<get<mycount>(t)<<endl;
		myclasst2<mycount+1, mymaxcount, T...>::mysfunc(t)
	}
};

template<int mymaxcount, typename ...T>
class myclasst2<mymaxcount, mymaxcount, T...>{
public:
	static void mysfunc(const tuple<T...> &t){
	}
};


template<typename ...T>
void myfunctuple(const tuple<T...> &t){
	myclasst2<0, sizeof...(T), T...>::mysfunc(t);
}

tuple<float, int, int> mytuple(12.5f, 100, 52);
myfunctuple(mytuple);

15.7.3 模板模板参数

模板参数本身是一个模板。

template<
	typename T,
	//template<class> class Container
	template<typename W> typename Container//W可省略
>

class myclass{
public:
	myclass(){
		for(int i=0; i<10; i++)
			myc.push_back(i);
	}
public:
	T m_i;
	Contianer<T> myc;
};
template<typename T>
using MYVec = vector<T, allocator<T>>;

template<typename T>
using MYList = list<T, allocator<T>>;

myclass<int, MYVec> myvecobj;
myclass<int, MYList > mylistobj;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值