C++模板进阶

1.typename的双重性

在模板中typename和class是相同的,但是在有一种情况下是不同的

我们来看这两串代码

这两个类型的区别无非在于

一个是明确的类型vector<int>

一个是不明确的模板类型T

编译器在检查

    vector<int>::const_iterator it = v.begin();


    T::const_iterator it = v.begin();

编译的时候不知道T::const_iterator到底是类型还是变量(对象)

而且也不知道去哪找(这个地方的T,编译的时候没有实例化)

就有两种情况

1.静态成员变量(对象)

2.内部类或者typedef嵌套的类型

像这个地方 你很难确定这个x是一个指针还是说const_iterator这个变量乘x 

包括下面的这个T::TT既可以是内部类也可也是静态变量!

​
class T
{
public:
	class TT
	{
	};
private:
	static int a;
};
int main(void) {
	T::TT a;//T::TT是内部类
	return 0;
}

​

 

class T
{
public:
	static int TT;
};

int main(void)
{
	T::TT;//T::TT静态变量
	return 0;
}

我怎么知道  T::const_iterator 是类型还是变量

如果是类型 那没问题

但是如果是变量呢? 那这个代码就有问题了呀!

但是vector<int>::const_iterator it明确告诉我们要去vector<int>这个域里面去找it

并且可以判断vector<int> iterator是一个明确的类型 而不是变量

但是对于T::const_iterator ,这个时候我们在前面+一个typename就可以了

typename的意思就是告诉编译器这里是类型,等模板实例化后再去找

(这个地方就不可以用class了)

但是思考一下这个地方我直接auto:it可不可以?

当然可以 auto的作用是推断出类型 也就意味着默认这个it就是类型而不是变量了!

但是在有些地方我们也不能用auto比如 我要去类模板里面取内嵌的值

 

typename的一般性规则

template<typename C>
void print1st(const C& container)
{
    if (container.size() > 1)
    {
        C::const_iterator iter(container.begin());//局部变量iter是从属名称
        int value = *iter;//局部变量value是非从属名称
        std::cout << value;
    }
}

 template 内出现的名称(代码中的const_iterator)如果相依于某个 template参数(代码中的C),称之为从属名称(dependent names)。如果从属名称在class内呈嵌套状,我们称它为嵌套从属名称(nested dependent name)。C::const_iterator就是这样一个名称。实际上它还是个嵌套从属类型名称(nested dependent type name),也就是个嵌套从属名称并且指涉某类型。

***缺省情况下嵌套从属名称***

 

2. 非类型模板参数

#define N=100;
template<class T>
class Stack
{
private:
	T _a[N];
	T _top;
};

int main(void)
{
	Stack<int> s1;
	Stack<int> s2;
	return 0;
}

我们来看这段代码,这个地方就不是很灵活 比如说我s1要开10个int空间

s2要开20个空间怎么办呢?

你可能说可以通过函数传N 但是静态数组的那个N是常量啊!
模板提供了一个很好的方式,模板的参数除了类型,还可以传变量(非类型形参

template<class T,size_t N>
class Stack
{
private:
	T _a[N];
	T _top;
};

int main(void)
{
	Stack<int,10> s1;
	Stack<int,20> s2;
	return 0;
}

这样就可以更好的实现泛型编程了

但是同样也有许多要注意的

1.传的参数只能是整型

2.传的参数在模板中将参数当作常量来使用

像有些容器用的就是非类型形参

3.array

 

array是C++11的一个容器是一个定长数组

和普通的数组比 它对越界的检查非常严格 越界读写都能检查

普通数组 不能检查越界读 部分越界写可以检查 

和vector<int>比  array在栈区上  vector<int>在堆区上  且array不用扩容

比vector<int>少一个capacity

4.模板特化

我们对模版的一些特例有时候要特殊处理

函数模板选择的方法很多 比如函数重载

template<class T>
bool Less(T left, T right)
{
	return left < right;
}

//特殊处理方式1 函数重载   优先级最高
bool Less(int* left, int* right)
{
	return left < right;
}
//特殊处理2 函数重载  //优先级再其次
template<class T>  
bool Less(T* left, T* right)
{
	return left <right;
}

//特殊处理3 模板特化
template<>      //优先级其次
bool Less(int* left, int* right)
{
	return left < right;
}

int main(void)
{
	cout << Less(1, 2) << endl;
	int a = 3, b = 1;
	cout << Less(&a, &b) << endl;
	//像这个地方如果我们直接这样就是比较两个地址的大小
	//但是如果我们想比较这两个指针所指向的数的大小就要去特殊处理
	return 0;
}

那么对于类模板呢?类可不能重载 类就只能用模板特化了

template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<T1,T2>" << endl;
	}
};

//特例
template<>
class Date<int,double>
{
public:
	Date()
	{
		cout << "Date<int,double>" << endl;
	}
};
int main(void)
{
	Date<int,int> d1;
	Date<int, double>d2;
	//这个地方我们想让d2这个地方变成一个特例  
	//打印的是Date(int,double)而不是Date<T1,T2>
	return 0;
}

特化的前提必须有原模版

特化是在原模板的基础上进行特化

 全特化就是所有模板参数特化

偏特化(半特化)有两个作用

(1)对部分模板参数进行特化

(2)对某些类型进行一些限制

template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<T1,T2>" << endl;
	}
};

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

//半特化(片特化) 特化部分参数
// 第二个模板参数是double就走这个 但是对于<int,double>优先级不如上面那个全特化
template<class T1>
class Date<T1,double>
{
public:
	Date()
	{
		cout << "Date<T1,double>" << endl;
	}
};

//半特化(片特化) 对某些类型的进一步限制
//这个地方就特化 计算只匹配<指针,指针>
template<class T1,class T2>
class Date<T1*, T2*>
{
public:
	Date()
	{
		cout << "Date<T1*,T2*>" << endl;
	}
};

int main(void)
{
	Date<int,int> d1;
	Date<int, double>d2;
	//这个地方我们想让d2这个地方变成一个特例  
	//打印的是Date(int,double)而不是Date<T1,T2>
	Date<double, double>d3;
	return 0;
}

严格来说模板特化的类 不算新的类 因为它不独立存在

但是模板特化类的内容可以和原类不一样 包括成员函数和变量

但是不一样容易出问题 按照需求写就可以了

但是你实例化出来的东西算新的类型(就像vector<int>和vector<double>是两个类型一样)

5.模板的定义声明分离

这个地方为什么push不行?

原因是push没有实例化 编译器怎么知道这个T是什么类型 怎么生成地址给call呢?

怎么解决?

在函数定义的文件显示实例化

template
class stack<int>;

但是这个方式有坑

如果我要的不算stack<int>而是stack<double>呢?

那就要再写一个,治标不治本 !

最好的办法就是把定义和声明写在同一个文件内

6.模板总结

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值