c++primer之模板与泛型编程

一、模板定义以关键字template开始,后跟一个模板参数列表,模板参数列表以逗号分隔,用小于号和大于号包围起来

二、模板参数前使用关键字class或typename

三、一个非类型参数表示一个值而非一个类型,通过一个特定的类型名而非关键字class或typename

四、一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或引用。绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期

五、函数模板可以声明为inline或constexpr,放在模板参数列表之后,返回类型之前

六、为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,所以模板的头文件通常包括声明和定义

七、编译器不能为类模板推断模板参数类型

八、定义在类模板内的成员函数被隐式声明为内联函数

九、定义在类模板之外的成员函数必须以关键字template开始,后跟类模板参数列表

十、在类模板自己的作用域中,可以直接使用模板名而不提供实参

十一、当一个类包含一个友元声明时,类与友元各自是否是模板是相互无关的。如果一个类模板包含一个非模板友元,则友元被授权可以访问所有模板实例。如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例

十二、模板声明必须包含模板参数

十三、如果希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型,通过使用关键字typename来实现

十四、如果一个类模板为其所有模板参数都提供了默认实参,且我们希望使用这些默认实参,就必须在模板名后跟一对空尖括号

十五、一个类可以包含本身是模板的成员函数,称为成员模板,成员模板不能是虚函数

十六、当在类模板外定义一个成员模板时,必须同时为类模板和成员模板提供模板参数列表。类模板的参数列表在前,后面跟成员自己的模板参数列表

十七、显式实例化声明: extern template declaration

十八、一个类模板的实例化定义会实例化该模板的所有成员,包括内联的成员函数

十九、显式模板实参在尖括号中给出,位于函数名之后,实参列表之前

二十、可以使用remove_reference来获取元素类型,remove_reference模板有一个模板类型参数和一个名为type的类型成员。如果用一个引用类型实例化remove_reference,则type表示被引用的类型:remove_reference<decltype(*beg)>::type. decltype(*beg)返回元素类型的引用类型,remove_reference::type脱去引用,剩下元素类型本身

二一、当一个函数参数是模板类型参数的一个普通引用时,只能传递给它一个左值,实参可以时const类型,也可以不是; 当一个函数参数的类型是const T&,可以传递给它任何类型的实参;当一个函数参数是一个右值引用,可以传递给它一个右值

二二、通常不能将一个右值引用绑定到一个左值上,但有2个例外:

1、当经一个左值传递给函数的右值引用参数,且此右值引用指向模板类型参数时,编译器推断模板类型参数为实参的左值引用类型

2、如果间接拆功能键一个引用的引用,则这些引用形成折叠,引用折叠成一个普通的左值引用类型,新标准可以折叠成右值引用,即右值引用的右值引用

(1)、T& &,T& &&, T&& &都折叠成类型T&

(2)、类型T&& &&折叠成T&&

二三、实际开发中,右值引用通常用于2中情况: 模板转发其实参或模板被重载

二四、虽然不能隐式的将一个左值转换为右值引用,但可以通过static_cast显式的将要给左值转换为一个右值引用

二五、通常情况下,使用forward传递传递那些定义为模板类型参数的右值引用的函数参数,通过其返回类型上的引用折叠,forward可以保持给定实参的左值/右值属性

二六、函数模板可以被另一个模板或一个普通非模板函数重载

二七、一个可变参数模板就是一个接受可变数目参数的模板函数或模板类,可变数目的参数被称为参数包。存在2中参数包: 模板参数包;函数参数包

二八 、用一个省略号来指出一个模板参数或函数参数表示一个包。在一个模板参数列表中,class...或typename...指出接下来的参数表示0个或多个类型的列表;一个类型名后面跟一个省略号表示0个或多个给定类型的非类型参数的类别

二九、当需要知道包中有多少给元素时,可以使用sizeof...运算符,类似sizeof运算符,sizeof...也返回一个常量表达式,且不会对其实参求值

三十、可以组合使用可变参数模板与forward机制来实现将其实参不变的传递给其他函数

三一、当特例化一个函数模板时,必须为原模板中的每个模板参数都提供实参,为了指出正在实例化一个模板,应使用关键字template后跟一个空尖括号对,空尖括号指出将为原模板的所有模板参数提供实参

三二、当定义一个特例化版本时,函数参数类型必须与一个先前声明的模板中对应的类型匹配

三三、一个特例化版本本质上时一个实例而非函数名的一个重载版本

三四、可以向命名空间添加成员,首先需要打开命名空间: namespace std {}

 

template<typename T>
int compare(const T& v1, const T& v2)
{
	//if (v1 < v2) return -1;
	//if (v2 < v1) return 1;

	if (less<T>() (v1, v2)) return -1;
	if (less<T>() (v2, v1)) return 1;
	return 0;
}

template<typename T, typename F = less<T>>
int compare2(const T& v1, const T& v2, F f = f())
{
	if (f(v1, v2)) return -1;
	if (f(v2, v1)) return 1;
	return 0;
}

template<unsigned N, unsigned M>
int compare1(const char(&p1)[N], const char(&p2)[M])
{
	return strcmp(p1, p2);
}
//函数模板特例化, 特例化compare(const T& v1, const T& v2)
template<>
int compare(const char* const &p1, const char* const &p2)
{

}

template<typename> class BlobPtr;
template<typename> class Blob;
template<typename T> bool operator==(const Blob<T>&, const Blob<T>&);

template<typename T>
class Blob
{
	friend class BlobPtr<T>;
	friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
public:
	typedef T value_type;
	typedef typename vector<T>::size_type size_type;

	// 类模板的成员模板
	template <typename I>
	Blob(I b, I e);

	Blob();
	Blob(initializer_list<T> il);

	size_type size() const { return data->size(); }
	bool empty() const { return data->empty(); }
	
	void push_back(const T& t) { data->push_back(t); }
	void push_back(T& t) { data->push_back(move(t)); }
	void pop_back();

	T& back();
	T& operator[](size_type i);

private:
	shared_ptr<vector<T>> data;
	void check(size_type i, const string& msg) const;
};
template<typename T>
void Blob<T>::check(size_type i, const string& msg) const
{
	if (i >= data->size()) {
		throw 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");
	return data->pop_back();
}
// 类模板的参数列表在前,后跟成员自己的模板参数列表
template <typename T>
template<typename I>
Blob<T>::Blob(I b, I e) : data(make_shared<vector<T>>(b, e))
{

}
template<typename T>
Blob<T>::Blob() : data(make_shared<vector<T>>())
{

}
template<typename T>
Blob<T>::Blob(initializer_list<T> il) : data(make_shared<vector<T>>(il))
{

}


template <typename 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<T>& operator++(); 
	BlobPtr& operator++(); // 在类模板自己的作用域中,可以直接使用模板名而不提供实参
	BlobPtr& operator--();

private:
	shared_ptr<vector<T>> check(size_t, const string&) const;
	weak_ptr<vector<T>> wptr;
	size_t curr;
};


template<typename T>
typename T::value_type top(const T& c)
{
	if (!c.empty()) {
		return c.back();
	}
	return typename T::value_type();
}

template<class T= int> 
class Numbers
{
public:
	Numbers(T v = 0) : val(v) {}

private:
	T val;
};

class DebugDelete
{
public:
	DebugDelete(ostream& s = cerr) : os(s) {}
	// 普通类的成员模板
	template<typename T>
	void operator()(T* p) const
	{
		os << "deleting unique_ptr" << endl;
		delete p;
	}

private:
	ostream& os;
};

// 尾置返回类型
template <typename T>
auto fcn(T beg, T end) ->decltype(*beg)
{
	return *beg;
}

// decltype(*beg)返回元素类型的引用类型,remove_reference::type脱去引用,剩下元素类型本身
template <typename T>
auto fcn2(T beg, T end) ->typename remove_reference<decltype(*beg)>::type
{
	return *beg;
}

// 当一个函数参数是模板类型参数的一个普通引用时,只能传递给它一个左值,可以是const的,也可以不是
template<typename T>
void f1(T&);
//f1(intA); // 正确, 模板参数T是int
//f1(constIntA); // 正确,模板参数T是const int
//f1(5);// 错误, 传递给一个&参数的实参必须是一个左值

// 一个函数参数的类型是const T&,可以传递给它任何类型的实参
template<typename T>
void f2(const T&);
//f2(intA); // 正确, 模板参数T是int
//f2(constIntA); // 正确,模板参数T是const int
//f2(5);// 正确, 一个const&参数可以绑定到一个右值,T是int

// 当一个函数参数是一个右值引用,可以传递给它一个右值
template<typename T>
void f3(const T&&);
//f3(5); // 正确,实参是一个int类型的右值,模板参数T是int

// move定义
template<typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type&&>(t);
}

// 模板重载
template<typename T>
string debug_rep(const T& t)
{
	ostringstream ret;
	ret << t;
	return ret.str();
}
template<typename T>
string debug_rep(T* p)
{
	ostringstream ret;
	ret << "pointer:" << p;
	if (p) {
		ret << " " << debug_rep(*p);
	}
	else {
		ret << " null pointer";
	}
	return ret.str();
}

// 可变参数模板
template<typename T, typename... Args>
void foo(const T& t, const Args&... args);

template<typename... Args>
void g(Args... args)
{
	cout << sizeof...(Args) << endl; // 类型参数的数目
	cout << sizeof...(args) << endl; // 函数参数的数目
}

// 用来终止递归并打印最后一个元素
template<typename T>
ostream& print(ostream& os, const T& t)
{
	return os << t;
}
template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args&... args)
{
	os << t << ", ";
	return print(os, args...);
}

// 可以向命名空间添加成员
namespace std {
	template<>
	struct hash<int>
	{

	};
}

// 显式实例化
extern template class Blob<string>;
extern template int compare(const int&, const int&);
int main()
{
	cout << compare(1, 0) << endl;
	cout << compare1("hi", "mom");

	Blob<int> squares = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	for (size_t i = 0; i != squares.size(); ++i) {
		squares[i] = i * i;
	}

	Numbers<long double> lots;
	Numbers<> log; // 空<>表示使用默认类型

	// 普通类的成员模板
	double* p = new double;
	DebugDelete d;
	d(p);
	int* ip = new int;
	DebugDelete()(ip);

	// 类模板的成员模板
	int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	vector<long> vi = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	list<const char*> w = { "now", "is", "the", "time" };
	Blob<int> a1(begin(ia), end(ia));
	Blob<int> a2(vi.begin(), vi.end());
	Blob<string> a3(w.begin(), w.end());

	// 显式实例化
	Blob<string> sa1, sa2;
	Blob<int> a5;
	int i = compare(1, 2);

	int j = compare<long>(1.1, 2.2); // 显式模板实参

	int fi = 0;
	double fd = 3.14;
	string fs = "how now brown cow";
	foo(fi, fs, 43, fd); // 可变参数包含3个参数
	foo(fd, fs);// 可变参数包含1个参数
	foo(fd);// 空包

	print(cout, fi, fs, 42);
	// 第一次 print(cout, fi, fs, 42) t: fi,  args..: fs, 42
	// 第二次 print(cout, fs, 42) t: fs,  args..: 42
	// 第三次 print(cout, 42) 调用非可变参数版本的print

	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值