不会模板,还真不敢说自己懂C++,不过对于平时项目比较少的小伙伴而言,即便爆肝学习了整个模板教程,过不了多久也就忘了,因而想写一个工具书,用时翻翻即可。
模板分为函数模板和类模板,声明语句分别如下:
template <typename T>
T sum(T t1, T t2);
template <typename T>
class mData;
其中typename在大多数情况下可替换为class,不过个人还是习惯于在特定的场合使用typename或class,这样代码的可读性会更好。
目的是将相同功能的函数模板化
书写方法:
template <class 或 typename T>
返回类型 函数名(形参表)
{
函数体
}
template <typename T>
T sum(T & t1, T & t2)
{
return t1+t2;
}
非也!模板函数表示的是由一个函数模板生成而来的函数。
//模板函数
sum<int>(12,34);
sum<float>(1.3f,2.4f);
typedef string T;//这个地方无所谓
template <typename T>//这里的T是模板参数,而不是string的别名
T sum(T & t1, T & t2)
{
//typedef double T;这是不被允许的,因为T与模板参数同名。
T ans = t1+t2;
return ans;
}
template <typename T,typename T>//错误!模板参数名T的非法重复定义
T sum(T & t1, T & t2);
template <typename T1,T2>//错误!模板参数名T2的非法定义
T sum(T1 & t1, T2 & t2);
先举个例子吧!
class A{
public:
void func(){
cout<<"A:func()"<<endl;
}
};
template<class T>
void foo(){
A a;
a.func();//已知类型调用
T t;
t.func();//未知类型调用
t.what();//未知类型调用
}
void main(){
foo<A>();
}
其实foo发生了两次编译:
class A{
public:
template<class T>
void foo(){...}
}
template<class D>
void Func(){
D d;
d.foo<int>();//错误,对于未知类型的调用认为都合理 ,除非带有<>。
//怎么解决呢,有套路,虽然很丑
d.template foo<int>();//正确
}
template<class T>
T max(T x,T y){...}
int max(int x,int y){...}
void main(){
int x = 10,y = 20;
cout<<max(x,y)<<endl;//选择普通函数
cout<<max<>(x,y)<<endl;//选择函数模板,<>强制选择
double dx = 1.2,dy = 2.3;
cout<<max(dx,dy)<<endl;//选择函数模板
cout<<max(x,dy)<<endl;//选择普通函数,隐式类型转换
}
template <class 或 typename T>
class 类名
{
...
}
个人习惯:为了避免模板关键字class与类关键字class混淆,推荐使用typename,PS:当然实际上编译器是认识的不会混淆,不过用成typename更清晰不是!
template <typename T>
class mData
{
private:
T _val;
public:
explicit mData(T & val) : _val(val) { }
explicit mData(T && val) : _val(val) { }
void add(T & t)
{
_val += t;
std::cout<<_val<<std::endl;
}
void sub(T& t);
};
//成员函数在外部实现
template<typename T>
void mData<T>::sub(T& t){
_val -= t;
std::cout<<_val<<std::endl;
}
需要注意的是在类定义体外定义成员函数时,还需在类体外进行模板声明,比如上面的sub函数。
非也!模板类表示的是由一个类模板生成而来的类。正式一些:模板类是由类模板实例化而成的一个类,这也就是2.5节所要讨论的内容。
//模板类
mData<int>
mData<float>
mData<double>
后面的这两点是相当重要的,下面以一个例子说明:
template<class T>
class CMath{
private:
T x,y;
public:
CMath(T x,T y):x(x),y(y){}
void add(){
T sum = x + y;
cout<<sum<<endl;
}
}
class CPoint{
private:
int x,y;
public:
CPoint(int x,int y):x(x),y(y){}
}
void main(){
CMath<double> obj(12.3,45.6);//到目前为止,成员函数add()并没有被实例化
obj.add();//这个时候add()才被实例化
//怎么样理解第三点呢
CPoint p1(12,34),p2(45,67);
CMath<CPoint> obj2(p1,p2);//CPoint并没有实现+这个功能,但是依然可以实例化CMath。有什么好处呢,那就多多了,假如CMath包含几百种数学操作,如果没有这一性质,想要实例化CMath,CPoint也必须支持这些功能,比如exp2,sin,cos,但明显不需要也不可能吧,CPoint只需要简单的加减操作就行,这样,只要CPoint支持加减操作,就可以实例化自己需要的CMath模板类。
}
先看下面一段代码,看有什么问题:
template<typename T>
class A{
private:
A<void> a;//递归,进入死循环
}
void main(){
A<int> a;//报错!提示使用正在定义的A
}
学过递归的肯定知道,递归需要一个出口,在这里采用特化来终结递归。
template<>
class A<void>{...}
template<int N>
class Sum{
public:
enum
{
//这里用枚举类型,因为用变量不会在编译期间推导值
value = N + Sum<N-1>::value>;
};
};
//特化,想停在哪里就特化在哪里
template<>
class Sum<0>{
public:
enum
{
value = 0;
};
};
对了,模板递归在处理可变参数模板是很有用的,见3.6.4节。
宏直接就可以产生代码,而编译器遇到模板定义时,并不产生代码,只有当模板实例化后才会产生代码。
C++中模板的实例化指函数模板(类模板)生成模板函数(模板类)的过程。对于函数模板而言,模板实例化之后,会生成一个真正的函数。而类模板经过实例化之后,只是完成了类的定义,模板类的成员函数需要到调用时才会被初始化。模板的实例化可以是隐式的(编译器生成的)或显式的(用户提供的)。
隐式实例化应该是我们的默认选择。隐式实例化意味着编译器会自动使用提供的模板实参生成具体的函数或类。一般来说,编译器也会从函数的实参中推导出模板实参。在 C++17 中,编译器也可以推导出类模板的模板实参。部分内容与3.7节类似,可移步阅读。
我们已经知道可以使用任何类型来实例化类模板,那么由类模板实例化产生的类也可以用来实例化类模板本身,这种做法称之为类模板的递归实例化
通过这种方法可以构建空间上具有递归特性的数据结构(比如:多维数组)。
template<int N,class T>
class Array{
public:
T& operator [](size_t i){
return *(m_arr+i);
}
private:
T m_arr[N];
}
void main(){
Array<20,int> a;
a[6] = 7;
}
熟悉STL的小伙伴就知道了,这我们经常用啊!
vector<vector<int>> vc(20,vector<int>(10,0));
typename关键字有两种用法:
下面是STL中basic_string定义的一小段代码,正是typename的第二种用法:
class basic_string
{
public:
typedef typename allocator_type::value_type value_type;
typedef typename allocator_type::pointer pointer;
typedef typename allocator_type::const_pointer const_pointer;
typedef typename allocator_type::reference reference;
typedef typename allocator_type::const_reference const_reference;
typedef typename allocator_type::size_type size_type;
typedef typename allocator_type::difference_type difference_type;
...
}
template <class T>
class allocator
{
public:
//内置数据类型
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
}
问题是假如不这样写会发生什么情况呢?就是下面的
class A{
public:
class B{
public:
void foo(){...}
}
};
template<class T>
void Func(){
T::B b;//错误,编译器并不会认为B是一个类,而是把它当成T的成员变量
//正确的应该是:typename T::B b;
b.foo();
}
特化是与泛化相对应的,泛化是一般化,而特化就是特殊化。对谁特殊化呢?当然是类型参数。
全特化:将类模板/函数模板的模板参数列表中的所有模板参数做特殊化!(说人话:将模板参数列表中所有的模板参数全部指定为确定的类型)。
//下面是全特化版本的模板类 以及 模板函数的定义
template<>//全特化标识
//全特化版本的模板类的definition
class 类名<具体参数>{
public:
//...
}
//全特化版本的模板函数的definition
template<>//全特化标识
返回类型 函数名(具体参数){
//...
}
同样是给出basic_string实现的例子:
template <class CharType>
class char_traits
{
typedef CharType char_type;
static size_t length(const char_type* str)
{
size_t len = 0;
for (; *str != char_type(0); ++str)
++len;
return len;
}
...
}
//全特化版本
// Partialized. char_traits
template <>
class char_traits<char>
{
typedef char char_type;
static size_t length(const char_type* str) noexcept
{ return std::strlen(str); }
}
对于编译器而言,在模板实例化过程中,由于特化版本提供了更精准的实现,因而优先考虑特化版本。
template<typename T, typename U>
class A {//类模板
public:
A() {
cout << "这是泛化版本的A类构造函数!" << endl;
}
void testfunc() {
cout << "泛化版本!" << endl;
}
~A() {
cout << "泛化版本的A类的析构函数!" << endl;
}
};
//特化版本的成员函数testfunc()
template<>
void A<char, char>::testfunc() {
cout << "泛化版本的A类模板的特化成员函数void A::testfunc()!" << endl;
}
int main(void) {
A<char, char> t;//创建泛化版本的对象
t.testfunc();
//因为我定义了特化版本的void A::testfunc()
//所以这里编译器会优先调用特化版本的成员函数testfunc() !!!
return 0;
}
将模板中的模板参数部分指定为确定的类型。
template<typename T, typename U>
class A {//类模板
public:
A() {
cout << "这是泛化版本的A类构造函数!" << endl;
}
void testfunc() {
cout << "泛化版本!" << endl;
}
~A() {
cout << "泛化版本的A类的析构函数!" << endl;
}
};
template<typename T>
class A<T,float> {//类模板
public:
A() {
cout << "这是偏特化版本的A类构造函数!" << endl;
}
void testfunc() {
cout << "偏特化版本A!" << endl;
}
~A() {
cout << "偏特版本的A类的析构函数!" << endl;
}
};
这里面提到了一点,可以从模板参数范围这个角度来进行偏特化。
举例:
本来参数类型是int:
参数范围缩小:
int->const int
int->int*
int->int&
int->int&&
int->const int*
参数范围增大:
const int->int
int*->int
int&->int
int&&->int
类模板的静态数据成员既不是每个对象拥有一份,也不是类模板拥有一份,而是由类模板实例化出的每一个真正的类各有一份,且为该实例化类定义的所有对象共享。
类模板静态数据成员分为以下两种:
template<typename T>
class A {
static T s;//依赖于模板类型参数的静态成员
}
//只能进行特化
template<>
float A<float>::s = 13.14f;
//只能进行特化
template<>
char A<char>::s = '0';
//只能进行特化
template<>
double A<double>::s = 1314520;
//只能进行特化
template<>
int A<int>::s = 1314;
void main(){
cout<<A<float>::s<<endl;
cout<<A<char>::s<<endl;
cout<<A<double>::s<<endl;
cout<<A<int>::s<<endl;
}
template<typename T>
class A {
static int s;//不依赖于模板类型参数的静态成员
}
template<typename T>
int A<T>::s = 520;//模板类的静态成员变量初始化,对任意类型的T都有一份通用的值
//对s进行特化
template<>
int A<int>::s = 1314;
void main(){
cout<<A<float>::s<<endl;
cout<<A<char>::s<<endl;
cout<<A<double>::s<<endl;
//以上输出均为520
cout<<A<int>::s<<endl;//输出为1314
}
也就是在模板类中再嵌入一个模板类,比如下面的:
template<typename T1>
class Outer {
private:
T1 t1;
public:
Outer(T1& t1):t1(t1){}
void print();
template<typename T2>
class Inner{
private:
T2 t2;
public:
Inner(T2& t2):t2(t2){}
void print();
}
}
template<typename T1>
void Outer<T1>::print(){
cout<<"Outer Print:"<<t1<<endl;
}
template<typename T1>
template<typename T2>
void Outer<T1>::Inner<T2>::print(){
cout<<"InnerPrint:"<<t2<<endl;
}
void main(){
//外部类
Outer<int> O(23);
O.print();
//内部类
Outer<int>::Inner<float> I(6.5f);
I.print();
}
类模板用到继承时需要注意:
// 类模板与继承
template<class T>
class A{
public:
T m;
void funA(){...}
};
//普通子类继承类模板
class B:public A<int> {//这里要明确指定T的类型
public:
...
}
// 灵活指定父类中 T 类型,子类也需要变成类模板
template<class T1, class T2>
class C:public A<T2>{
public:
C() {
cout << typeid(T1).name() << endl;
cout << typeid(T2).name() << endl;
}
T1 obj;
//访问基类中的成员或成员函数
funA();//错误!!!第一次编译的时候public A不是真正意义上的类,而继承以及访问基类的成员是C++类的特性。
//编译器不会去基类中找funA的声明,类模板没有继承这个说法
//怎么解决呢?
A<T>::funA();//加入作用域限定符
//或者
this->funA();//其实这两种都是利用了编译器处理未知类型调用,第一次编译先容忍的机制。
};
void test(){
C<int, char>s2;
}
int main(){
test();
system("pause");
return 0;
}
顾名思义就是表示一个类型的参数,将类型参数化。
template<class T>
void h(T a){};//其中T就是一个类型形参,类型形参的名字由用户自已确定。
template<class T>
void h(T a, T b){}
void main(){
h(2, 3.2)//错误!!!因为该语句给同一模板形参T指定了两种类型
}
顾名思义,就是表示一个固定类型的常量而不是一个类型
template<class T, int a>
class B{};//其中int a就是非类型的模板形参。
//整形包括int,char,long,unsigned,bool,short等可转化为整形的类型
//像double,String, String **这样的类型是不允许的。
//但是double &,double *,对象的引用或指针是正确的。
1、比如说想要定义一个动态数组,即由用户指定长度的数组类,因为数组的长度需要在编译的时候就确定,这个时候就需要非类型模板参数,来指定数组的长度,在实例化这个模板数组的时候把数组的长度传进去!!!
//由用户自己亲自指定栈的大小,并实现栈的相关操作
//TemplateDemo.h
#ifndef TEMPLATE_DEMO_HXX
#define TEMPLATE_DEMO_HXX
template<class T,int MAXSIZE>
class Stack{//MAXSIZE由用户创建对象时自行设置
private:
T elems[MAXSIZE]; // 包含元素的数组
int numElems; // 元素的当前总个数
public:
Stack(); //构造函数
void push(T const&); //压入元素
void pop(); //弹出元素
T top() const; //返回栈顶元素
bool empty() const{ // 返回栈是否为空
return numElems == 0;
}
bool full() const{ // 返回栈是否已满
return numElems == MAXSIZE;
}
};
template <class T,int MAXSIZE>
Stack<T,MAXSIZE>::Stack():numElems(0){ // 初始时栈不含元素
// 不做任何事情
}
template <class T,int MAXSIZE>
void Stack<T, MAXSIZE>::push(T const& elem){
if(numElems == MAXSIZE){
throw std::out_of_range("Stack<>::push(): stack is full");
}
elems[numElems] = elem; // 附加元素
++numElems; // 增加元素的个数
}
template<class T,int MAXSIZE>
void Stack<T,MAXSIZE>::pop(){
if (numElems <= 0) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
--numElems; // 减少元素的个数
}
template <class T,int MAXSIZE>
T Stack<T,MAXSIZE>::top()const{
if (numElems <= 0) {
throw std::out_of_range("Stack<>::top(): empty stack");
}
return elems[numElems-1]; // 返回最后一个元素
}
#endif
模板参数的类型本身也是一个模板,就是模板模板参数
// 模板类 queue
// 参数一代表数据类型,参数二代表底层容器类型,缺省使用 mystl::deque 作为底层容器
template <class T, template <typename> class Container = mystl::deque>
class queue
{
public:
...
}
template <typename> class
//或者
template <class> class
另外我也看到有这样写模板模板参数的,先贴出来,表述不准确,下面的不是模板模板参数,但是实现的目的却很类似:
// 模板类 stack
// 参数一代表数据类型,参数二代表底层容器类型,缺省使用 mystl::deque 作为底层容器
template <class T, class Container = mystl::deque<T>>
class stack
{
// 使用底层容器的型别
typedef typename Container::value_type value_type;
typedef typename Container::size_type size_type;
typedef typename Container::reference reference;
typedef typename Container::const_reference const_reference;
static_assert(std::is_same<T, value_type>::value,
"the value_type of Container should be same with T");
}
大家看,是否与下面的功能类似:
template <class T, template <typename> class Container = mystl::deque>
class stack
{...}
都给了缺省值deque,区别就是前者加了< T >,而后者没有;前者deque< T >是模板类,是一个类型,而后者是一个模板。不过还是推荐后者,前者类中需要大量的约束和判断来使得Container有效,毕竟就外人看来,它接受的就是个普通的类型参数;而后者指明了是模板模板参数。
template<typename T>
void func(T arg){
cout<<arg<<endl;
}
template<typename T,typename ... Types.
void func(T firstArg,Types ... args){
cout<<sizeof...(args)<<endl;//通过sizeof...(args)可获取到变参个数
func(firstArg);
func(args...);
}
调用C++的模板函数时,由编译器根据上下文来推断所调用的模板函数的模板参数。假设我们有下面这个模板函数:
tempalte<typename T>
void f(T& param){ // param is a reference
cout<<typeid(T).name()<<endl;//打印类型
}
int x = 27; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
//使用类型推导,也就省的用<>了
f(x) // T is int, param's type is int&
f(cx) // T is const int, param's type is const int&
f(rx) // T is const int, param's type is const int&
从这儿也就引出了下面的内容:
template<class T>
T max(T x,T y){...}
max(123,456); -> max<int>(123,456);
获得和普通调用函数一致的语法表现形式。
类型推导的规律:
大名鼎鼎的remove_cv用的就是上述性质:
#include
#include
int main()
{
int *p = (std::remove_cv_t<const volatile int> *)0;
p = p; // to quiet "unused" warning
std::cout << "remove_cv_t == " << typeid(*p).name() << std::endl;
//输出为:remove_cv_t == int
return (0);
}
在rb_tree的实现过程中,MyTinySTL也用了remove_cv:
typedef typename std::remove_cv<typename T::first_type>::type key_type;
关于类型推导,下面给出几个推荐用法:
额,这也是为了完整性,没什么好说的,显式推断也就是直接给出。
max<int,int>(123,456);
以下三种情况不能做隐式推断
// 1、调用参数和类型参数不完全相关,例如
template<class T,class D>
T max(T x,T y){...}
// 2、隐式推导不支持隐式类型转换,例如
template<class T>
T max(T x,T y){...}
max(123,45.6f)//出错
// 3、返回值类型不支持隐式推断
问题:
1、基本类型不存在缺省构造函数,未被初始化的局部变量都具有一个不确定的值。
2、类类型由于存在缺省构造函数,在未被初始化的情况下可以有一个确定的缺省初始化状态。
解决方法:
如果希望模板中,所有类型参数的变量,无论是类类型还是基本类型都以缺省方式获得初始化,就必须对其进行缺省的显示构造。
T t = T();//推荐
T t;//容易出现没有初值的未知错误
这个技巧在std::vector的实现里面也用到了,下面贴一小段代码:
template <class T>
class vector
{
public:
// vector 的嵌套型别定义
typedef mystl::allocator<T> allocator_type;
typedef typename allocator_type::value_type value_type;
...
public:
explicit vector(size_type n)
{ fill_init(n, value_type()); }//此处用到了零值初始化技巧
}
还可以用在默认值的设定
// resize / reverse
void resize(size_type new_size) { return resize(new_size, value_type()); }
void resize(size_type new_size, const value_type& value);
std::initializer_list< T> 类型对象是一个访问 const T 类型对象数组的轻量代理对象。用花括号初始化列表器构造的均可视作initializer_list,可用来初始化vector、list、deque、queue、stack等。
vector(std::initializer_list<value_type> ilist)
{
range_init(ilist.begin(), ilist.end());
}
list(std::initializer_list<T> ilist)
{ copy_init(ilist.begin(), ilist.end()); }
deque(std::initializer_list<value_type> ilist)
{
copy_init(ilist.begin(), ilist.end(), mystl::forward_iterator_tag());
}
vector& operator=(std::initializer_list<value_type> ilist)
{
vector tmp(ilist.begin(), ilist.end());
swap(tmp);//这样效率要比赋值快得多
return *this;
}
// 与另一个 vector 交换
template <class T>
void vector<T>::swap(vector<T>& rhs) noexcept
{
if (this != &rhs)
{
mystl::swap(begin_, rhs.begin_);
mystl::swap(end_, rhs.end_);
mystl::swap(cap_, rhs.cap_);
}
}
先不解释,直接贴一段代码。
vector(vector&& rhs) noexcept
:begin_(rhs.begin_),
end_(rhs.end_),
cap_(rhs.cap_)
{
rhs.begin_ = nullptr;
rhs.end_ = nullptr;
rhs.cap_ = nullptr;
}
// 移动赋值操作符
template <class T>
vector<T>& vector<T>::operator=(vector&& rhs) noexcept
{
destroy_and_recover(begin_, end_, cap_ - begin_);
begin_ = rhs.begin_;
end_ = rhs.end_;
cap_ = rhs.cap_;
rhs.begin_ = nullptr;
rhs.end_ = nullptr;
rhs.cap_ = nullptr;
return *this;
}
size_type max_size() const noexcept
{ return static_cast<size_type>(-1) / sizeof(T); }
template <class Ty>
void construct(Ty* ptr)
{
::new ((void*)ptr) Ty();
}
template <class Ty1, class Ty2>
void construct(Ty1* ptr, const Ty2& value)
{
::new ((void*)ptr) Ty1(value);
}
vector<T>::emplace(const_iterator pos, Args&& ...args)
{
...
if (end_ != cap_ && xpos == end_)
{
data_allocator::construct(mystl::address_of(*end_), mystl::forward<Args>(args)...);
++end_;
}
}
若目标在源之前开始,则从缓冲区开始正向复制,否则从末尾反向复制,完全无重叠时回落到更高效的 std::memcpy 。
从末尾反向复制,这一点在插入元素时简直妙极!
vector<T>::emplace(const_iterator pos, Args&& ...args)
{
...
else if (end_ != cap_)
{
auto new_end = end_;
data_allocator::construct(mystl::address_of(*end_), *(end_ - 1));
++new_end;
mystl::copy_backward(xpos, end_ - 1, end_);//内部调用的正是std::memmove
*xpos = value_type(mystl::forward<Args>(args)...);
end_ = new_end;
}
...
}
template <class T>
bool operator==(const vector<T>& lhs, const vector<T>& rhs)
{
return lhs.size() == rhs.size() &&
mystl::equal(lhs.begin(), lhs.end(), rhs.begin());
}
template <class T>
bool operator<(const vector<T>& lhs, const vector<T>& rhs)
{
return mystl::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), lhs.end());
}
/*****************************************************************************************/
// lexicographical_compare
// 以字典序排列对两个序列进行比较,当在某个位置发现第一组不相等元素时,有下列几种情况:
// (1)如果第一序列的元素较小,返回 true ,否则返回 false
// (2)如果到达 last1 而尚未到达 last2 返回 true
// (3)如果到达 last2 而尚未到达 last1 返回 false
// (4)如果同时到达 last1 和 last2 返回 false
/*****************************************************************************************/
template <class InputIter1, class InputIter2>
bool lexicographical_compare(InputIter1 first1, InputIter1 last1,
InputIter2 first2, InputIter2 last2)
{
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
{
if (*first1 < *first2)
return true;
if (*first2 < *first1)
return false;
}
return first1 == last1 && first2 != last2;
}
// 重载版本使用函数对象 comp 代替比较操作
template <class InputIter1, class InputIter2, class Compred>
bool lexicographical_compare(InputIter1 first1, InputIter1 last1,
InputIter2 first2, InputIter2 last2, Compred comp)
{
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
{
if (comp(*first1, *first2))
return true;
if (comp(*first2, *first1))
return false;
}
return first1 == last1 && first2 != last2;
}
// 针对 const unsigned char* 的特化版本
bool lexicographical_compare(const unsigned char* first1,
const unsigned char* last1,
const unsigned char* first2,
const unsigned char* last2)
{
const auto len1 = last1 - first1;
const auto len2 = last2 - first2;
// 先比较相同长度的部分
const auto result = std::memcmp(first1, first2, mystl::min(len1, len2));
// 若相等,长度较长的比较大
return result != 0 ? result < 0 : len1 < len2;
}
enable_if可能的实现为:
template<bool B, class T = void>
struct enable_if {};
//偏特化版本
template<class T>
struct enable_if<true, T> { typedef T type; };
若 B 为 true ,则 std::enable_if 拥有等同于 T 的公开成员 typedef type ;否则,无该成员 typedef 。下面给出一小段代码阐释enable_if的用法:
template <class Iter, typename std::enable_if<
mystl::is_input_iterator<Iter>::value, int>::type = 0>
vector(Iter first, Iter last)
{
MYSTL_DEBUG(!(last < first));
range_init(first, last);
}
用在上面的代码中,如果mystl::is_input_iterator< Iter>::value为假,则编译不通过。注意模板声明中的::type=0不能省略。
1、C++之:模板元编程(二) 模板形参
2、Th4.6:模板全特化、偏特化(局部特化)详述
3、C++类模板
4、模板类型推导
持续更新中,水平有限,欢迎大家批评指正