16 C++ 模板与泛型编程

系列文章目录



前言

模板是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<T>()(v1, v2)) return -1
	if (v2 < v1) return 1;  //        if(less<T>()(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<int>

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

个人感觉还是加上<T>好些

类模板和友元
在这里插入图片描述
一对一友元关系
在这里插入图片描述

通用和特定的模板友好关系——普通类和模板的友元关系

//前置声明,在将模板的一个特定实例声名为友元时要用到
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<C>;
	template<class T> friend class-name;
模板类T:
	friend class Pal<T>;
	template <class X> friend class Pal2;
	friend class Pal2;

在这里插入图片描述
模板类型别名

typedef Blob<string> StrBlob;
由于模板不是一个类,不能定义一个typedef引用一个模板。不可:typedef Blob<T> 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<string>::ctr和Foo<string>::count
Foo<string> fs;
//所有三个对象共享相同的Foo<int>::ctr和Foo<int>::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<int>::Blob(int*, int*);

控制实例化

当模板被使用时才会进行实例化,这一特新意味着,相同的实例可能出现在多个对象文件中。
在大系统中,在多个文件中实例化相同的模板的额外开销可能非常严重。
显式实例化

extern template declaration;	//实例化声名
template declaration;			//实例化定义
extern template class Blob<string>; 			//声名
template int compare(const int&, const int&);   //定义

效率与灵活性

运行时绑定删除器    在编译时绑定删除器


总结

模板:
  函数模板:

template<typename T, class U,...> return-type  functionname(para-list);
        typename T, class U,...为模板形参
        实例化模板并搞个对象: vector<int> vec;
        实例化:functionname(4); 类型由编译器推断

  类模板:

template<typename T> class class-name;
        实例化:类型事先指定 class-name<T>;

模板的友元问题较为复杂,包含 函数,类,模板三者之间的友元关系

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值