【Json11源码阅读】07 问题解答,Part_5
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
上一节只是对模板有个大概的了解,还没能解答上面的问题,我们继续看书
模板类型别名
可以使用typedef来引用实例化的类
typedef Blob<string> StrBlob;
但是无法使用typedef
为Blob<T>
定义一个引用,因为模板不是一个类型
但是可以使用using
template<typename T> using twin = pair<T, T>;
twin<string> authors;
当定义一个模板类型别名时,可以固定一个或多个模板参数
template<typename T> using partNo = pair<T, unsigned>;
partNo<string> books;
类模板的static成员
普通类的static成员有且仅有一个定义,即多个实例共享。
但是对于类模板的static成员来说,每个实例都有一个独有的static对象。
注意这里说的实例并不是指类的对象,而是模板实例
Foo<string> fs;
Foo<int> fi, fi2, fi3;
fs和fi之间有各自的static成员,但fi、fi2和fi3共享一份static成员
因为static成员也是带类型的
template<typename T>
size_t Foo<T>::ctr = 0;
模板参数的作用域
- 模板参数会隐藏外层作用域中声明的相同名字
- 在模板内不能重用模板参数名
typedef double A;
template<typename A, typename B> void f(A a, B b)
{
A tmp = a;
double B;
}
- 模板参数A隐藏了外层typedef定义的类型别名A,因此在函数模板内部A是一个类型,不是double。
- 而B将模板参数名用作变量名,是错误的。
由于模板参数名不能重用,所以在同一个模板参数列表中也只能出现一次
template<typename V, typename V>
错误
模板声明
模板声明必须包含模板参数
template<typename T> class Blob;
使用类的类型成员
对于string::size_type
,由于编译器有string的定义,所以知道size_type
是一个类型成员
但是对于T::mem
,编译器就无法判断mem是一个类型成员还是一个static数据成员
因此,我们必须告诉编译器,T表示一个类型
template<typename T>
typename T::value_type top(const T& c)
{
return typename T::value_type();
}
也就是说,使用类模板的类型成员时,必须在模板参数名前加上typename
关键字
之前提取typename关键字和class关键字是一样的,在这里并不适用,必须使用typename
关键字
默认模板参数
在新标准中,我们可以为函数和类模板提供默认实参(更早的标准只允许为类提供)
template<typename T, typename F = less<T>>
int compare(const T &v1, const T &v2, F f = F())
{
if(f(v1,v2))
{
return -1;
}
return 0;
}
模板参数列表中包含两个参数,这里为第二个参数提供了默认实参,F是一个可调用对象,默认为less<T>
函数
同时也为对应的函数参数提供了默认实参,默认函数实参指出f将是类型F的一个默认初始化的对象
类模板默认实参
可以为类模板提供默认实参
template<class T = int> class Numbers
{
public:
Numbers(T v = 0) : val(v){}
private:
T val;
};
如果想要使用类的默认模板实参,尖括号必须保留
Numbers<> average_precision;
Numbers<long double> lots_of_precision;
成员模板
本身是模板的成员函数称为成员模板。
成员模板不能是虚函数
- 普通类的成员模板
- 模板类的成员模板
普通类的成员模板
class DebugDelete
{
public:
DebugDelete(std::ostream &s = std::cerr) : os(s) {}
template<typename T>
void operator()(T *p) const
{
os << "deleting unique_ptr" << std::endl;
delete p;
}
private:
std::ostream &os;
};
该类重载了一个调用运算符,它接受一个指针并对此指针执行delete
。由于希望能对任意类型的指针执行删除操作,所以定义为了函数模板
在使用时需要借助DebugDelete
类的对象来使用
double *p = new double;
DebugDelete d;
d(p);
int *ip = new int;
DebugDelete()(ip);
也可以作为unique_ptr
的删除器
unique_ptr<int, DebugDelete> p(new int, DebugDelete());
我们在p的构造函数中传入了一个未命名的DebugDelete
对象,unique_ptr
的析构函数会调用DebugDelete
的调用运算符。
类模板的成员模板
在此情况下,类和成员各自有独立的模板参数
template <typename T> class Blob
{
template <typename It>
Blob(It b, It e);
};
我们知道函数模板会根据传入的实参类型自动推断实例类型,但是类模板需要明确指定
Blob<int> a1(begin(ia), end(ia));
END
微信公众号:马志峰的编程笔记
记录一名普通程序员的成长之路