模板是C++中泛型编程的基础。一个模板就是一个创建类或函数的蓝图或公式。当使用一个vector这个样的泛型类型,或find这样的泛型函数时,我们提供足够的星系,将蓝图转换为特定的类或函数。这种转换发生在编译时。
function template 模板参数列表template parameter list 模板参数template parameter 模板实参template argument
template <typename T> int compare(const T &v1, const T &v2){
if (v1 < v2) return -1; //高移植性 if(less()(v1, v2)) return -1
if (v2 < v1) return 1; // if(less()(v2, v1)) return 1
return 0;
}
//实例化
cout << compare(1, 0) << endl; //T为int
vector<int> vec1{1,2,3}, vec2{4,5,6};
cout << compare(vec1, vec2) << endl; //T为vector
template<typename T, class U> void calc(const T&, const U&);
非类型模板参数
nontype parameter
template<unsigned N, unsigned M> int compare(const char (&p1)[N], const char (&p2)[M]){
return strcmp(p1, p2);
}
调用compare("hi", "mom")实例化->int compare(const char (&p1)[3], const char (&p2)[4])
非类型模板参数的模板实参必须是常量表达式
inline和constexpr
template<typename T> inline T min(const T&, const T&);
template <typename T> class Blob{
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
Blob() = default;
Blob(std::initializer_list<T> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const T &t) { data->pushback(t); }
void push_back(T &&t) { data->push_back(std::move(t)); }
void pop_back();
T& back();
T& operator[] (size_type i);
private:
std::shared_ptr<std::vector<T>> data; //T为模板Blob的形参,是模板vector的实参
void check(size_type i, const std::string &msg) const;
};
template<typename T>
void Blob<T>::check(size_type i, const std::string &msg) const{
if (i >= data->size())
throw std::out_of_range(msg);
}
template<typename T>
T& Blob<T>::back(){
check(0, "back on empty Blob");
return data->back();
}
template<typename T>
T& Blob<T>::operator[](size_type i){
check(i, "subscript out of range");
return (*data)[i];
}
template<typename T> void Blob<T>::pop_back(){
check(0, "pop_back on empty Blob");
data->pop_back();
}
template<typename T>
Blob<T>::Blob():data(std::make_shared<std::vector<T>>())
{ }
template<typename T>
Blob<T>::Blob(std::initializer_list<T> il):
data(std::make_shared<std::vector<T>>(il)) {}
实例化类模板
这样理解:模板实例化后就是类,类实例化就是对象(或者说类对象)
如 vector() 模板vector首先实例化为类vector,然后将类实例化为对象vector()
模板中的模板
类模板成员函数的实例化
如果一个成员函数没有被使用,则它不会被实例化。成员函数只有在被用到时才进行实例化。
在类代码内简化模板类名的使用
template<class T> class BlobPtr{
public:
BlobPtr(): curr(0) { }
BlobPtr(Blob<T> &a, size_t sz = 0): wptr(a.data), curr(sz) {}
T& operator*() const{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
BlobPtr& operator++();
BlobPtr& operator--();
private:
std::shared_ptr<std::vector<T>> check(std::size_t, const std::string&) const;
std::weak_ptr<std::vector<T>> wptr;
std::size_t curr;
};
在类模板外使用类模板名
template<typename T>
BlobPtr<T> BlobPtr<T>::operator++(int){
//此处无须检查;调用前置递增时会进行检查
BlobPtr ret = *this;
++*this;
return ret;
}
个人感觉还是加上
通用和特定的模板友好关系——普通类和模板的友元关系
//前置声明,在将模板的一个特定实例声名为友元时要用到
template <class T>class Pal;
class C{ //C是一个普通的非模板类
friend class Pal<C>; //通用类C实例化的Pal是C的一个友元
// Pal2的所有实例都是C的友元;这种情况无需前置声名
template<class T> friend class Pal2;
};
template <class T> class C2{ //模板类
//C2的每个实例将相同实例化的Pal声明为友元
friend class Pal<T>; //Pal的模板声名必须在作用域之内
//Pal2的所有实例都是C2的每个实例的友元,不需要前置声明
template <class X> friend class Pal2;
//Pal3是一个非模板类,他是C2所有实例的模板
friend class Pal3; //不需要Pal3的前置声名
};
普通类C:
friend class class-name;
template friend class-name;
模板类T:
friend class Pal;
template friend class Pal2;
friend class Pal2;
typedef Blob StrBlob;
由于模板不是一个类,不能定义一个typedef引用一个模板。不可:typedef Blob name;
template<typename T> using twin = pair<T, T>;
twin<int> iv; iv时一个pair<int, int>
类模板的static成员
template <typename T> class Foo{
public:
static std::size_t count() { return ctr; }
private:
static std::size_t ctr; //static声名 不是定义和初始化
};
//实例化static成员Foo::ctr和Foo::count
Foo<string> fs;
//所有三个对象共享相同的Foo::ctr和Foo::count成员
Foo<int> f1, f2, f3;
template<class T> size_t Foo<T>::ctr = 0; //定义并初始化ctr
模板参数与遵循普通的作用域规则 模板声名必须包含模板参数
//声名但不定义
template<typename T> int compare(const T&, const T&);
template<typename T> class Blob;
```cpp
template<typename T>typename T::value_type top(const T&c){
if (!c.empty())
return c.back();
else
return typename T::value_type(); //cann not be class
}
默认模板实参——像函数的默认实参一样
template <typename T, typename F = less<T>> int compare(const T&v1, const T&v2, F f = F()){
if (f(v1, v2)) return -1;
if (f(v2, v1)) return 1;
return 0;
}
bool i = compare(0, 42);
Sales_data item1(cin), item2(cin);
bool j = compare(item1, item2, compareIsbn);
模板类/类包含的成员是个模板函数
普通类的成员模板
class DebugDelete{
public:
DebugDelete(std::ostream &s = std::cerr): os(s) { }
//与任何模板函数相同,T的的类型由编译器推断
template<typename T> void operator()(T *p) const
{ os << "deleting unique_ptr" << std::endl; delete p; }
private:
std::ostream &os;
};
double *p = new double;
DebugDelete d; //可像delete表达式一样使用的对象
d(p); //调用DebugDelete::operator()(double*),释放p
int *ip = new int;
//在一个临时DebugDelete对象上调用operator()(int*)
DebugDelete()(ip);
类模板的成员模板
template<typename T> struct Blob{
template<typename It> Blob(It b, It e);
//
};
template<typename T> template<typename It> Blob<T>::Blob(It b, It e):
data(std::make_shared<std::vecotr<T>>(b, e) ) { }
实例化与成员模板
int ia[] = {1,2,3,4};
vector<long> vi = {1,2,3};
list<const char*> w = {"as", "sdf"};
Blob<int> al(bein(ia), end(ia));
Blob<int> a2(vi.begin(), vi.end() );
Blob<string> a3(w.being(), w.end());
//Blob::Blob(int*, int*);
当模板被使用时才会进行实例化,这一特新意味着,相同的实例可能出现在多个对象文件中。
在大系统中,在多个文件中实例化相同的模板的额外开销可能非常严重。
显式实例化
extern template declaration; //实例化声名
template declaration; //实例化定义
extern template class Blob<string>; //声名
template int compare(const int&, const int&); //定义
运行时绑定删除器 在编译时绑定删除器
模板:
函数模板:
template return-type functionname(para-list);
typename T, class U,...为模板形参
实例化模板并搞个对象: vector vec;
实例化:functionname(4); 类型由编译器推断
类模板:
template class class-name;
实例化:类型事先指定 class-name;
模板的友元问题较为复杂,包含 函数,类,模板三者之间的友元关系