【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;  

但是无法使用typedefBlob<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;  

模板参数的作用域

  1. 模板参数会隐藏外层作用域中声明的相同名字
  2. 在模板内不能重用模板参数名
typedef double A;  
template<typename A, typename B> void f(A a, B b)  
{  
    A tmp = a;  
    double B;  
}  
  1. 模板参数A隐藏了外层typedef定义的类型别名A,因此在函数模板内部A是一个类型,不是double。
  2. 而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


微信公众号:马志峰的编程笔记

记录一名普通程序员的成长之路

posted @ 2017-04-18 07:16  马志峰  阅读(281)  评论(0编辑  收藏  举报