C++ primer 第十六章笔记 初稿
来源:互联网 发布:清华北大抢人 知乎 编辑:程序博客网 时间:2024/06/13 03:34
16.1 模板定义
typename 关键字在模板被广泛使用后才引入,尽管指示更加清楚,但class仍然被许多人使用
非类型模板参数
要求:可以是一个整型/指针/左值引用,但必须是常量或有静态生存期(针对指针和引用)
指明参数是一个数组:
如果函数定义为如下形式,当传入字符串常量时,T会默认转换成字符数组template<class T,int N>void print(T r[N]) {...}
如果想使用,则必须显示提供字符串长度,未达到最初目的
int array[6] = {1, 2, 3, 4, 5, 6};print<int, 6>(array);
可以将函数定义修改为如下形式,显示说明传递数组
template<class T,int N>void print(T (&r)[N]) {...}
这样就可以直接使用
int array[6] = {1, 2, 3, 4, 5, 6};print(array);
两个重要原则
- 函数参数应当是const引用(可以传递不能拷贝的对象)
- 条件判断一般都使用<运算符
模板若想生成一个实例,必须已知定义。
注:使用模板的函数声明或使用模板定义一个指针或引用,都不是实例化
定义在类模板之内的函数,被隐式定义为内联函数(inline)
一般在模板类外定义的函数,形式如下
template<typename T> ret-type class_name<T>:: f(parm_list)
模板类的成员函数只有在使用时才被实例化,因此即使一个实例不完全支持模板的所有操作,也可以使用该模板
一个省略操作:在模板类中,返回类型若为类本身,则无需提供实参 < T > (不提供反而很习惯)
模板类的友元
一个类若包含非模板友元,则该友元必然可以访问模板的所有实例
如果友元也是模板,其访问权限由模板类授权决定
如下方法可建立实例类型一对一(即类型相同)的友元关系
template<typename T> class A{ friend class B<T>; friend void f<T>(...); ...}
如下方法可以建立其他类型友元
class A{...}; template<typename> template<typename> class B { template<typename X> friend class A; //B的所有实例都是A的所有实例的友元 friend T; //T是D<T>的友元 };
- 类型别名
使用using可以很方便的“固定”一个或多个类型参数
template <typename T> using twins = std::pair<T, T>;
twin<int> int_twin;
twin<double> double_twin; 静态成员:不同实例有不同的静态成员,但同类型实例只有一个静态成员,所以要访问一个静态成员,必须显示指明类型。
如果要访问在模板的类型参数中定义的类型别名,需在类型参数前显示地加上typename
template <typename T>typename T::val_type *top; //top是val_type类型T::val_type *top //默认是相乘
- 新标准下(C++11)可以为函数和类模板提供默认实参,但必须遵照默认实参的规则(右侧参数均有默认实参)
当成员函数模板与类模板参数互相独立时,类外定义应两次注明template,而不是合并它们
template<typename T1>template<typename T2>A<T> :: f(T2 b, T2 e){...}
显示实例化
- 原因:同一个模板在多个文件中被实例化,造成浪费
- 方式:
- 声明:extern template class A;
- 定义:template class A;
- 注意:如果显示实例化一个类模板,必须可用于所有的成员
16.2 实参推断
类型确定与转换
- 基本转换:
- 顶层const被忽略,非const对象可传递给const引用形参
若形参非引用形式,则可以将数组转换为指针
template <typename T> void f1(T)template <typename T> void f2(const T&);int a[10];f1(a); //f1(int *);f2(a); //非法
显示确定:即在名字后使用尖括号注明,省略规则即尾部忽略。
置尾返回类型的使用
- 面临的问题:
- 模板中的返回类型在遇到参数列表前不可知(如返回一个形参同类的迭代器)
- 有时返回类型应该是值,但迭代器只能返回解引用
- 解决办法
- 置尾返回
- 使用remove_reference(头文件:type_traits)
template <typename T>auto f(T begin, T end) -> typename::remove_reference<decltype(*begin)>::type{ return *begin;}
- 面临的问题:
引用折叠
面临的问题:
- 有时形参是右值引用,但实参是左值
- 通过类型别名,有时会产生引用的引用
机制:
- X& &&, X & &, X&& & 折叠为 X&
- X&& && 折叠为 X&&
实际效果
template <typename T> void f(T &&);int i = 1;f(i); //T 为 int&f(i * 2); //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 F, typename T1, typename T2>void filp1(F f, T1 t1, T2 t2){ f(t2, t1);}
但如果f希望改变实参,则出现问题
void f(int v1, int &v2){ cout<<++v2<<v1<<endl;}
因此将模板函数的定义修改为接收右值引用(可以保留引用的底层const属性)
template <typename F, typename T1, typename T2>void filp2(F f, T1 &&t1, T2 &&t2){ f(t2, t1);}
但如果f希望获得一个右值引用,则出现问题,因为函数参数是一个左值表达式,而左值无法转化为右值
void f(int &&v1, int &v2){ cout<<++v2<<v1<<endl;}
因此需要使用forward
- 头文件:unity
- 效果:返回参数的右值引用
所以最终filep应该为template <typename F, typename T1, typename T2>void filp(F f, T1 &&t1, T2 &&t2){ f(std::forward<T2> t2, std::forward<T1>t1);}
16.3 模板重载
基本匹配原则:
- 如果有且仅有一个非模板函数同样好,选择非模板函数
- 如果有多个模板函数同样好,选择最特例化的一个,例如
template <typename T> f(const T&);template <typename T> f(T *);string s = "...";string *p = &s;f(p); //调用第二个版本
- 如果直接将字符串作为参数带入模板,优先判定为char*
16.4 可变参模板
- 表示形式
- 模板参数列表中,typename…表示多个类型,type…表示多个给定类型的非类型参数
- 函数中形参的表述方式如下:
template <typename... Args>
void f(const Args&... rest){...}
可以用 sizeof 访问参数的数目。
通常可以用递归的方式处理可变参模板
template <typename T, typename...Args>ostream& f(ostream& os, const T& t, const Args& ... args) { os<<t<<"\t"; return f(os, args...); //包扩展 }
包扩展
- 形式:在模式后加…,即上述中的调用操作
- 如果想多次调用某个函数,则形式为
template <typename T, typename...Args>ostream& print(ostream& os, const T& t, const Args& ... args) { return f(os, g(args)...);}
16.5 模板特例化
函数模板特例化
遇到的问题:当处理字符串时,尽管已经重载了不同版本的成员函数,可以在传递字面常量和数组时做出区分,但如果传递字符指针,则无法达到目的。
解决办法:一般与声明在同一个文件中,提供实例化,即提供一个尖括号内为空的模板,并提供具体类型
注意细节:对于原来类型为const T&的形参,如果实例化位指针则为底层const,故应为const char * const
模板特例化
对于模板,可以对某一部分进行特例化,也可以对整体特例化#include <iostream>template<typename T> class S {public: void info() { printf("In base template\n"); } static int code; }; template<typename T> int S<T>::code = 10; template<> int S<int>::code = 100; //普通静态成员变量的int特化 template<> void S<int>::info() { //成员函数的int特化 printf("In int specialization\n"); } int main(){ S<float> s1; s1.info(); //普通版本 printf("Code is: %d\n", s1.code); //code = 10 S<int> s2; s2.info(); //int特化版本 printf("Code is: %d\n", s2.code); //code = 100 return 0;}
- C++primer 第十六章笔记 初稿
- C++ primer 第十六章笔记 初稿
- C++primer 第九章笔记 初稿
- C++primer 第十章笔记 初稿
- C++primer 第二章笔记 初稿
- C++primer 第三章笔记 初稿
- C++primer 第四章笔记 初稿
- C++primer 第五章笔记 初稿
- C++primer 第六章笔记 初稿
- C++primer 第十一章笔记 初稿
- C++primer 第十二章笔记 初稿
- C++primer 第十三章笔记 初稿
- C++primer 第十四章笔记 初稿
- C++primer 第十五章笔记 初稿
- C++ primer 第八章笔记 初稿
- c++primer plus第十六章-string类
- c++primer第五版第十六章练习
- c++ primer 学习笔记-第十六章
- Excel 技巧百例:删除空白行
- laravel一些细节优化
- hdu2029 Palindromes _easy version(C语言)
- Linux for Ubuntu安装Inkscape开源矢量图工具
- vb.net 教程 12-2 HtmlDocument类 1
- C++ primer 第十六章笔记 初稿
- Bootstrap入门
- Java Web工作原理
- STL容器的删除元素问题
- HDU
- 待补充
- springcloud(二)
- Using System Calls
- solr-架构