template//模板定义中,模板参数列表不能为空
- 一种是模板类型参数,类型参数前要有关键字
class或typename,在模板参数列表中,二者没有区别。- 另一种是非类型模板参数,非类型参数表示一个值而非一个类型。通过特定类型名,如
unsigned来指定,做两个长度不同的数组比较。非类型参数可以是一个整型(实参是常量表达式),或者是一个指向对象或函数类型的指针或(左值)引用(实参要具有静态生存期)。
inline/constexpr:template<typename T>inline T min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; }
当实例出模板的特定版本时,编译器才会生成代码。
考虑到生成实例化版本时,编译器需要掌握函数模板或类模板成员函数的定义,模板的声明和定义都会放到头文件中。
hhdy到hhdy)template<typename T>
class hhdyptr {
hhdyptr& operator++(); //类内可以简化模板类名
};
template<typename T>
hhdyptr<T>& hhdyptr<T>::operator++() {//类外不可以简化模板类名
hhdyptr hh = *this;//类内可以简化模板类名
++* this;
return hh;
}
//以关键字template开始,后跟模板参数列表
template <typename T>
class hhdy {
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
//构造
hhdy():data(std::make_shared<std::vector<T>>()){}
hhdy(std::initializer_list<T> il):data(std::make_shared<std::vector<T>>(il)){}
//元素数目
size_type size() const{
return data->size();
}
bool empty() const {
return data->empty();
}
//增删元素
void push_back(const T& t);
void push_back(T&& t) {
data->push_back(t);
}
void pop_back() {
data->pop_back();
}
//元素访问
T& back() {
return data->back();
}
T& operator[](size_type idx) {
if (check(idx,"out of range")) {
return ( * data)[idx];
}
return back();
}
private:
std::shared_ptr<std::vector<T>> data;
bool check(size_type i, const std::string& msg) const {
try {
if (i >= size())
throw std::out_of_range(msg);
return 1;
}
catch (std::out_of_range& err) {
std::cerr << err.what() << std::endl;
return 0;
}
}
};
template<typename T>
void hhdy<T>::push_back(const T& t) {
data->push_back(t);
}
hhdy<int> hh = { 1,2,3,4 }; //需要提供元素类型
hh.push_back(5);
std::cout << hh.size() << std::endl;
std::cout << hh[6]<< std::endl;
//声明特定实例为友元前需要前置声明
template <typename T>class hhdy;
template <typename T>class xx;
template <typename T>
class xx{
friend class hhdy<int>;
};
//将每个实例都声明为友元时不需要,友元声明中要使用与类模板本省不同的模板参数。
template <typename T>
class xx{
friend template <typename C> friend class hhdy;
};
//可以将模板参数声明为友元。
template <typename T>
class xx{
friend T;
};
typedef hhdy ihhdy; template<typename T> using twin = pair<T,T>;
twin<int> pairs; //pair
template<typename T> using partNo = pair<T,unsigned>;
partNo<int> xx; //pair
X,FOO类型对象共享静态对象和函数。class xx {
public:
xx() = default;
xx(T t):num(t){}
static void prt() {
std::cout << "999" << std::endl;
}
T num;
static std::size_t ctr;
};
template<typename T>size_t xx<T>::ctr = 0; //将static数据成员也定义为模板
int main(){
xx<int> xx1,xx2; //共享static成员,实例化xx类及内部静态成员
xx2.prt();
xx<int>::prt();//通过类模板访问要说明类的类型参数
std::cout << xx<int>::ctr << std::endl;
std::cout << xx2.ctr << std::endl;
}
T,当遇到T::size_type*p时,会不知道size_type是类型成员还是static成员,要通过关键字typename加以区分。此时有两种解释:
- 1.T::size_type 类型的p指针->
typename T::size_type *p- 2.静态成员size_type乘以p->
T::size_type*p
//函数
template<typename T,typename F = std::less<T>>
bool compare(const T& v1, const T& v2, F f = F()) {
return f(v1, v2);
}
//类模板
template<typename T = int>class A {
};
A<> b; //默认A
template<typename U>class A;
template<typename T = int>class A {
public:
typedef size_t A_type;
T calc(const T&, const T&);
//不是典型例子
typename A<T>::A_type capacity() {
return A<T>::cap;
}
private:
static size_t cap;
T t;
};
template<typename T> size_t A<T>::cap = 100;
template<typename T> T A<T>::calc(const T& a, const T& b) {
//double T; //报错,重声明模板参数名称
return a + b;
}
int main() {
A<int> a;
A<> b;
std::cout << a.calc(1,2) << std::endl;
std::cout << a.capacity() << std::endl;
std::cout << compare(3,2) << std::endl;
return 0;
}
//默认删除器
class DebugDelete {
public:
DebugDelete(std::ostream&s = std::cerr):os(s){}
template<typename T>void operator()(T* p)const { //成员函数模板
os << "delete unique_ptr" << std::endl;
delete p;
}
private:
std::ostream& os;
};
int main() {
//用来删除堆上指针
double* p = new double;
DebugDelete d;
d(p);
int* ip = new int;
DebugDelete()(ip);
//可以用做unique_ptr删除器,unique_ptr的析构函数会调用DebugDelete的调用运算符进行析构。
std::unique_ptr<int, DebugDelete> p1(new int, DebugDelete());
*p1 = 2;
std::cout << *p1 << std::endl;
return 0; //unique_ptr在函数结束后会进行析构并调用DebugDelete()
}
template<typename T>
class A {
public:
template<typename U>
U calc(const U&, const U&);
};
//类模板外定义成员模板
template<typename T>
template<typename U>
U A<T>::calc(const U& a, const U& b) {
return a + b;
}
intmain(){
A<int> a;
std::cout << a.calc(1,2) << std::endl; //3,推断为calc
return 0;
}
extern template declaration; //实例化声明
template declaration; //实例化定义
extern模板声明表示在程序其他位置存在实例化的非extern声明或定义,它不会在本文件中生成实例化代码。
对于一个给定实例化版本,可以有多个extern声明,但必须只有一个定义。
extern声明必须出现在使用此实例化版本代码之前。
两个程序必须链接在一起
//templatebuild.cc,函数非extern声明,进行实例化
template int compare(const int&,const int&);
template class Blob<string>;
//Application.cc,函数extern声明,不实例化
extern template class Blob<string>;
extern template int compare(const int&,const int&);
Blob<string> sa1,sa2;
int i = compare(1,2);
以智能指针里shared_ptr和unique_ptr为例:
| 区别 | shared_ptr | unique_ptr |
|---|---|---|
| 管理保存指针的策略 | 给予共享指针所有权的能力 | 独占指针 |
| 重载默认删除器的方式 | 创建或reset指针时传递给它一个可调用对象 | 必须以显式模板实参的形式提供删除器类型 |
| 删除器绑定方法 | 运行时绑定,能直接访问,保存一个指针或封装了指针的类 | 编译时绑定,删除器直接绑定在unique_ptr对象中 |
| 工作 | del?del(p):delete p; | del(p); |
| 优势 | 运行时绑定,重载删除器更方便 | 编译时绑定,避免间接调用删除器的运行时开销 |
模板实参推断:从函数实参来确定模板实参的过程被称为模板实参推断。
- 顶层cosnt无论是在形参还是实参中都会被忽略;
- 可以将一个非const对象的引用或指针传递给一个const的引用或形参。
- 可以将数组和函数转化为数组指针和函数指针。
template<typename T>T fobj(T t1, T t2) { //拷贝模式
return t1 ;
}
template<typename T>T fref(const T& t1, const T& t2) {
return t1 ;
}
template<typename T,typename K>bool fcom(T t1, K k1) { //拷贝模式
return t1>k1 ;
}
template<typename T>bool fcompare(T t1, T t2) { //拷贝模式
return t1>t2 ;
}
int main() {
std::string s1("a value");
const std::string s2("another value");
std::cout<<fobj(s1, s2)<<std::endl;
std::cout << fref(s1, s2) << std::endl; //s1可以转化为const形式
int arr1[4] = { 1,2,3,4 };
int arr2[6] = { 1,2,3,4,5,6 };
std::cout << fobj(arr1, arr2) << std::endl; //输入变为数组指针
//std::cout << fref(arr1, arr2) << std::endl; //错误,数组类型不匹配
long lng = 1024;
fcom(lng,1024); //正确
fcompare(lng,1024); //错误,不知道转换为还是
return 0;
}
template<typename A,typename B,typename C> A sum(B b,C c);
auto val3 = sum<long long>(i,lng);
template<typename A,typename B,typename C> C sum(A a,B b);
auto val = sum<int>(i,lng); //错误,不能推断A类型
auto val2 = sum<int,long,long long>(i,lng); //正确
template<typename T>bool fcompare(T t1, T t2) { //拷贝模式
return t1>t2 ;
}
fcompare<int> (i,lng); //实例化fcompare
template<typename It>
auto fcn(It beg,It end) -> decltype(*beg) //使用尾置返回类型
{
return *beg;
}
//使用
std::vector<int> vi = { 1,2,3,4 };
auto& i = kfcn(vi.begin(), vi.end()); //int&
#include//使用typename告知编译器type表示一个类型
template<typename It>
auto kfcn(It beg,It end) ->
typename std::remove_reference<decltype(*beg)>::type
{
return *beg;
}
//返回元素值拷贝
auto i = kfcn(vi.begin(), vi.end());

template<typename T> int kcompare(const T&, const T&);
//可以用函数指针指向kcompare的一个实例
int (*pf)(const int&, const int&) = kcompare;
//对于存在多个重载的函数指针,需要调用特定实例版本的函数指针,需要用显式模板调用
void func(int(*)(const string&,const string&));
void func(int(*)(const int&,const int&));
func(compare<int>);
const T&类型,可以传递给它任何类型的实参(对象、临时对象或字面值常量等)。T&&),可以传递任意类型实参。左值变为左值引用,右值变为右值引用,引用会进行引用折叠。template<typename T> void A(T&&);
int i = 64;
A(i); //函数实参类型变为int&
const int ci = 5;
A(ci);//函数实参类型变为const int&
X& &, X& &&, X&& &都折叠成X&
X&& &&折叠成X&&
//不论接收的是什么类型,返回一个右值引用
template<typename T>
typename std::remove_reference<T>::type&& move(T&& t) {
return static_cast<std::remove_reference<T>::type&&>(t);
}
template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
return static_cast<remove_reference_t<_Ty>&&>(_Arg); //去引用后加右值引用,返回一个右值
}
static_cast 强制将对象类型转换为type,也可以显式的将一个左值转换为右值引用。
//翻转函数
template<typename F,typename T1,typename T2>
void flip(F f, T1&& t1, T2&& t2) {
std::cout<<f(std::forward<T2>(t2), std::forward<T1>(t1)) << std::endl;
}
- 形参都是右值引用形式:实参既可以是左值又可以是右值,并且进行了参数绑定
- std::forward:头文件
#include,std::forward,返回实参类型的右值引用std::forward//使用Type作为forward的显式模板实参类型,实际类型从arg中推断。arg是右值时,Type是普通类型,(arg); forward返回Type&&;当arg是左值,Type是左值引用,返回类型是指向左值类型的右值引用,折叠后为左值引用。
template <class _Ty>
_NODISCARD constexpr _Ty&& forward(
remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue
return static_cast<_Ty&&>(_Arg); //返回右值引用(可能折叠)
}
template<typename K>K ff(K1&& t1, K2& t2) {
return t1;
}
template<typename F,typename T1,typename T2>
void flip(F f, T1&& t1, T2&& t2) {
//ff(t2, t1); //错误,因为t2作为右值表达式是个左值,不能传入右值,需要将t2从右值表达式转换为右值再传入ff函数,所以要使用std::forward函数。
std::cout<<f(std::forward<T2>(t2), std::forward<T1>(t1)) << std::endl;
}
- 当有多个重载模板对一个调用提供好匹配时,选择最特例化的版本;
- 对于一个调用,如果一个非函数模板与一个函数模板提供同样好的匹配,选择非模板版本;
- 定义任何函数之前,要声明所有重载的函数版本。
#include
#include
#include
//1
template<typename T>std::string debug_rep(const T& t) {
std::ostringstream ret;
ret << t;
return ret.str();
}
//2
template<typename T>std::string debug_rep(T* p) {
std::ostringstream ret;
ret <<"poiter: " << p;
if (p)
ret << " " << debug_rep(*p);
else
ret << " null pointer";
return ret.str();
}
//3
std::string debug_rep(const std::string &s) {
return '"' + s + '"';
}
//4
std::string debug_rep(char* p) {
std::cout << "char" << std::endl;
return debug_rep(std::string(p));
}
//5
std::string debug_rep(const char* p) {
std::cout << "const char" << std::endl;
return debug_rep(std::string(p));
}
//声明
template<typename T>std::string debug_rep(const T& t);
template<typename T>std::string debug_rep(T* p);
std::string debug_rep(const std::string& s);
std::string debug_rep(char* p);
std::string debug_rep(const char* p);
int main() {
std::string s("hi");
std::string* sp = &s;
char strs[] = "hello world";
std::cout << debug_rep(s) << std::endl;//可匹配1/3,最终匹配3
std::cout << debug_rep(sp) << std::endl;//可匹配1/2,匹配到第2个版本
std::cout << debug_rep(strs) << std::endl;//会匹配到2,但不对,要进行精确匹配(增加4)
return 0;
}
#include
#include
#include
template<typename T,typename... Args>
void getsize(const T& t, const Args& ... rest) {
std::cout << sizeof...(Args) << std::endl; //3,返回参数包中参数数目
std::cout << sizeof...(rest) << std::endl;//3
}
//需要定义/声明递归结束时的非可变参数版本防止无限递归
template<typename T> std::ostream& print(std::ostream& os, const T& t) {
return os << t;
}
template<typename T,typename... Args> //扩展Args,扩展模板参数包
std::ostream& print(std::ostream& os,const T& t, const Args&... rest) { //扩展rest,扩展出一个有包种元素组成的,逗号分隔的列表,是函数调用生成实参列表
if constexpr (sizeof...(rest) == 0) //c++17语法,按条件编译,有这一条就不需要非可变参数版本了
return os << t;
else {
os << t << ", ";
return print(os, rest...);
}
}
int main() {
int i = 0;
double d = 3.14;
std::string s = "hhdy";
getsize(std::cout,i, d, 42, s);
print(std::cout,i, d, 42, s)<<std::endl;
return 0;
}
class hhdy {
public:
friend std::ostream& print_hh(std::ostream& os, const hhdy& hh);
hhdy(int n = 0,std::string str = "null"):i(n),s(str){}
private:
int i;
std::string s;
};
std::ostream& print_hh(std::ostream& os,const hhdy& hh) {
os << hh.s << " " << hh.i;
return os;
}
class hhdy_vec {
public:
friend std::ostream& print_hhv(std::ostream& os, const hhdy_vec& hhv);
hhdy_vec(){ std::vector<hhdy> vh; }
hhdy_vec(std::vector<hhdy> vii):vh(vii){}
template<class... Args>
void emplace_back(Args&&... args) {
vh.emplace_back( std::forward<Args>(args)...);
}
private:
std::vector<hhdy> vh;
};
std::ostream& print_hhv(std::ostream& os, const hhdy_vec& hhv) {
for (auto hh : hhv.vh) {
print_hh(os,hh)<<std::endl;
}
return os;
}
int main() {
hhdy_vec hhv;
hhv.emplace_back(60, "xx");
print_hhv(std::cout,hhv);
}
template<typename T>
void test(T&& i) {
std::cout <<"函数模板型版本test函数:" << i << std::endl;
}
template<>
void test(int&& i) {
std::cout << "int类型特例化版本test函数:" << i << std::endl;
}
template<size_t n, size_t m>
int getcom(const char(&t1)[n] , const char(&t2)[m]) { //注意模板参数写法
std::cout << "size_t类型特例化版本test函数:";
return strcmp(t1, t2);
}
int main() {
test(10);
test("hh");
- 一个重载的调用运算符,接受一个容器关键字类型的对象,返回size_t。
- 两个类型成员,result_type以及argyment_type,调用运算符的返回类型和参数类型
- 默认构造函数和拷贝赋值运算符
namespace std{
} //关闭命名空间,没有分号
template<class T>class std::hash; //友元声明前
class hhdy {
public:
friend struct std::hash<hhdy>; //声明为友元以访问对象
hhdy(int i, std::string s) :height(i), date(s) {}
private:
int height;
std::string date;
};
namespace std {
//参考struct std::hash;
//template <>
//struct hash {
// _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef double _ARGUMENT_TYPE_NAME;
// _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef size_t _RESULT_TYPE_NAME;
// _NODISCARD size_t operator()(const double _Keyval) const noexcept {
// return _Hash_representation(_Keyval == 0.0 ? 0.0 : _Keyval); // map -0 to 0
// }
//};
template<>
struct hash<hhdy> {
typedef size_t result_type;
typedef hhdy argument_type;
size_t operator()(const hhdy& hh)const {
return hash<int>()(hh.height) ^ hash<std::string>()(hh.date);
};
};
}
template<class T> struct remove_reference {
typedef T type;
};
template<class T>struct remove_reference<T&>{
typedef T type;
};
template<class T>struct remove_reference<T&&> {
typedef T type;
};
template<typename T>struct Foo{
Foo(const T &t = T()):mem(t){}
void Bar()
T mem;
}
template<>
void Foo<int>::Bar{} //当为Foo类时,成员函数Bar函数使用特例化版本进行不同操作