模板与泛型编程 c++ primer ch16.1
在摸板定义中,模板参数列表不能为空,
编译器用推断出的参数来进行 实例化(instantiation)
一般来说 模板是有type parameter
但是也可以声明 nontype parameter
template<unsgned N,unsigned M>
int compare(const char(&p) [n], const char(&p2)[M]){
return strcmp(p1,p2);
}
compare("hi","mom")
在执行时 ,实际上 实例化(instantiaton) 出的是 int compare(const char (&p)[3] ...
因为编译器会在字符串常量的末尾添加一个空字符作为终结符
- 一个非类型参数(nontype patameter) 可以是一个整形,或者是一个指向对象或函数类型的 指针 或者 (左值)引用
- 绑定到 非类型整形参数的实参 必须是一个常量表达式
- 绑定 指针或引用 的非类型参数 实参必须具有静态的生存期
- 不能使用一个普通(非 static ) 的局部变量或动态对象 作为 指针或引用 非类型模板参数的实参。
- 指针参数可以使用nullptr 或者 一个值为0 的常量表达式 来进行 实例化
+模板定义内 nontype parameter 是一个常值量 ,可以使用在需要长治表达式的地方 比如指定数组 的大小
可以声明为 inline 或者 constexpr 在模板参数列表之后 返回类型之前
编写泛型代码的两个重要原则###
- 模板中函数参数是const 引用
- 函数体的判断中仅仅使用了 < 比较操作
通过将函数参数设置为const 的引用 ,我们就保证了函数可以用于不能拷贝的对象上面
调用函数的时候 编译器需要掌握函数的声明,
所以函数声明和类定义都放在头文件里面。而函数的方法体与类的成员函数可以放在源文件中
模板则不同,为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,因此 模板的头文件 通常既包括声明也包括定义
模板大多到实例化期间才会 有编译错误
这主要在三个阶段报告错误
- 编译模板本身时,这一阶段检查语法错误
- 遇到模板使用时,检查参数匹配是否正确
- 模板实例化时 ,这一阶段发现类型相关的错误,可能在连接的时候才报告
编译器不能为类模板 推断模板参数类型 必须在模板名后的尖括号提供额外的信息---- 用来代替模板参数的模板实参列表
下面是一个列子
template <typename T> class Blob{
public:
typedef T value_type;
typedef_ typename std::vector<T>::size_type size_type; //这里 是说编译器在实例化之前 是不知道 vector<T>::size_type 是什么东西
//type_name 意在消除歧义,告诉编译器 size_type是一个类,而非一个成员变量
// 而事实上有三种可能 静态数据成员
// 静态成员函数
// 嵌套类
Blob();
Blob(initializer_list<T> il); // 这里是支持了 initializer 的语法的
size_type size()const{ return data->size() ; }
bool empt() const {return data->empty(); }
void push_back(const T& t)const {data->push_back(t);}
void push_back(T &&t)const {data->push_back(std::move(t));}
void pop_back();
T& back();
T& operator[](size_type i);
private:
std::shared_ptr<std::vector<T>> data;
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<tyepname T>
void Blob<T>::back()const {
check(0,"back on empty Blob");
return data->back();
}
template<typename T>
T& Blob<T>::operatpor[](size_type i){
check(1,"subscript out of range");
return (*data)[i];
}
在原 strBlob 中 下表运算符返回string& 而模板办吧呢
构造函数
Template<typename T>
Blob<T>::Blob():data(std::make_shared<std::vector<T>>()) {} //大概是分配了一个vector 指针 然后将vector 指针存贮在 data中
Template<typename T>
Blob<T>::Blob(initializer_list<T> il):data(std::make_shared<std::vector<T>>(il){} // 原理大概同上
默认情况下 ,一个类模板的成员只有在使用的时候才会实例化
这一特性使得即使某种类型不能完全符合模板操作的要求,我们仍然可以用该类型实例化类
在类代码内可以简化模板类名的使用
当我们使用类模板类型是 必须提供模板实参, 但是有一点例外
就是在类模板 自己的作用域中时
template class BlobPtr{
public:
BlobPtr():curr(0){}
BlobPtr(Blob &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--();// 这里的区别就是没有乐 模板参数 T乐因为在类的作用于内
private:
std::shared_ptr<std::vector> check(std::size_t,std::string&)const;
使用的是weak_ptr 表示底层的vector 可能被销毁
std::weak_ptr<std::vector> wptr ;
std::size_t curr;// 表示当前的位置
}
但是在函数体外部就必须加上模板参数乐
template
BlobPtr BlobPtr::operator++(int){
BlobPtr ret=this;// 这里的BlocPtr 并没有加上模板参数,是因为已经进入到了方法题的内部,所以不用使用参数了
++this;
return ret;
}
##类模板与友元##
类模板包含一个非模板友元, 友元可以访问所有的模板实例
如果友元自身是模板,泪可以授权给所有的友元模板 也可以只授权给特定的实例?
###一对一友好关系###
template class Blob;
template class BlobPtr;
template bool operator==(const Blob& ,const Blob &);
//以上是生命有远的一些声明前置
template class Blob{
firend class Blob ;
firend bool operator==(const Blob&,const Blob &);
}
###通用和特定的友好关系###
template class Pal; //前置声明
class C{
firend class pal; 使用c 生成的模板类属于C的友元
template class pal ; 这样 全部的模板类都是C的友 并且这种情况不需要前置声明
}
templateclass C2{
friend class pal; 一对一
template friend class pal; 一对多
}
**令模板自己的类型参数成为友元**
template <typename Type> class Bar{
friend Type;
//这样操作
}
**模板类型别名的使用方法**
template using twin = pair<T,T>
twin authors; 这个authors 是一个 pair<int,int>类型的变量
twin area 同理
还可以固定一个或者多个模板参数变量
template using twins= pair<T,unsiged>
twins books; 这里books 是一个pair<sting,unsigned> 类型的变量
类模板的静态成员
每一个模板类的实例中共享静态成员变量
vector<int> 中有一份静态变量
vector<double> 中也有一份静态变量
静态成员 变量的初始化
template<typename T>size_t Foo<T>:: ctr=0;
定义并且初始化ctr( 这是一个静态成员变量)
类似其余的成员变量,只有使用到的时候才会实例化成员变量或者是方法
模板参数遵循一般的变量规律,但是不能在模板内使用相同的模板参数名
###一个特定文件所需的所有模板的声明 通常一起放置在一个文件开始位置,出现于任何使用这些模板的代码之前。###
使用类的类型成员,编译器遇见
T::size_type *p;
要知道 T::size_type 是一个类型还是一个变量, 是类型就是声明,是变量就是相乘
编译器默认是认为是一个变量 所以如果是类型的化 需要显式的声明
下面是一个例子
template
typename T::value_type top( const T& c){
if(!c.empty){
return c.back();
}else{
return typename T::value_type();
}
}
上面的例子有值就返回值,没有就创建一个新的默认值对象返回
要通知编译器是一个类型的时候一定要使用typename 而不是class
##默认模板实参##
我们可以为函数和类模板提供默认实参,更早的版本中只有类模板可以
下面是一个函数对象模板的例子
template <typename T,typename F=less>
int compare(const &T&v1,cosnt T&v2,F f=F()){
if(f(v1,v2))return -1;
if(f(v2,v1))return 1;
return 0;
}
使用这个版本的compare,可以提供自己的比较器,但是并不是必须的,默认将使用 less<T>
可以使用空出的尖括号说明采用的都是 默认的类型参数
##成员模板##
成员模板不能是虚的
###普通类(非模板类)的成员模板###
下面的例子是一个关于删除器的例子
class DebugDelete{
public:
DebugDelete(std::ostream &s=std::cerr;
templatevoid operator()(T* p)const{
os<<"deleting unique_ptr"<<std::endl;
delete p;
}
private:
std::ostream &os;
}
// 上述定义的对象可以用作于 delete 相同的表现 ,删除任意类型的对象
###模板类的成员模板###
template class Blob{
template Blob (It b,It e);
}
要注意的是这样定义的成员模板在类外声明的时候必须提供两个的模板参数列表 如下
template template Blob (It a,It b):data(std::make_shared<std::vector>(b,e){} //像这样
与之前一致,我们在那个对象上调用了成员模板,就使用那块的参数进行推断并且实例化 initiation
##控制实例化##
在较大的系统中,多个源文件实例化同一个模板的代价非常的高,我们可以使用显式实例化来避免这种问题
explicit instanitaiton 如下
extern template declaration;// 实例化声明
template declaration; // 实例化定义
比如
extern template class Blob;
extern template int compare(const int&,const int&) ;
上面的定义和声明说明(承诺了) 在其他地方有一个已经初始化了的代码
,对于一个给定的实例化版本,可以有多个extern ,但必须只有一个定义
extern 必须出现在任何使用实例化版本的代码之前
Application.o 将包含Blob<int> 的实例化版本 ,但是不会包括Bolb<string>
的,如果另外一个文件templateBuld 包括 ,那在连接的时候就要将 templateBulid.o 同Application.o 进行连接
与普通方式不同,我们进行显式实例化时候,必须能够实例化类模板中的所有成员
模板与泛型编程 c++ primer ch16.1的更多相关文章
- C++ Primer(6) 模板和泛型编程(上)
问题聚焦: 泛型编程是独立于变量类型的方式编写代码: 模板是泛型编程的基础. 本篇主要介绍模板的基础知识,包括:模板的定义和模板的实例化. 1 模版定义 必要性: Demo int compare(c ...
- C++ primer 模板与泛型编程
继续浏览c++ primer 看到模板与泛型编程这章.就顺便把这几节的代码综合了下,对一个Queue队列模板的实现 贴一下代码(看完书.自己敲,忘记了哪再看下书) #include <ostre ...
- C++ Primer 学习笔记_76_模板与泛型编程 --模板定义[续]
模板与泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...
- C++ Primer 学习笔记_84_模板与泛型编程 --模板特化
模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...
- C++ Primer 学习笔记_77_模板与泛型编程 --实例化
模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实 ...
- C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]
模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还能够仅仅特化push和pop成员.我们将特化push成员以复制字符数组,而且特化pop成员以释放该副本使用的内存: ...
- C++ Primer 学习笔记_75_模板与泛型编程 --模板定义
模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模 ...
- C++ Primer 学习笔记_76_模板和泛型编程 --模板定义[继续]
模板和泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...
- C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型
模板与泛型编程 --模板编译模型 引言: 当编译器看到模板定义的时候,它不马上产生代码.仅仅有在用到模板时,假设调用了函数模板或定义了模板的对象的时候,编译器才产生特定类型的模板实例. 一般而言,当调 ...
随机推荐
- sulin Python3.6爬虫+Djiago2.0+Mysql --实例demo
1.切换到项目目录下,启动测试服务器 manage.py runserver 192.168.0.108:8888 2.设置相关配置 项目目录展示如下: beauty=>settings.py ...
- 三次面试总结以及今后的todolist
金三银四跳槽季,按耐不住蠢蠢欲动的跳槽心,投了好多家的前端招聘,目前面了三家,有把握的零家.古人吾日三省吾身,我没那么高的觉悟,三面省一下自身,太咸鱼了是的我就是这么觉得的. 第一家公司在景田,很远, ...
- js 验证图片
var selectedImg = e.target.files[0]; //获取图片 var isPic = /^(image\/bmp|image\/gif|image\/jpeg|image\/ ...
- .Net StackFrame
StackFrame指的是一个.net运行的时候堆栈上的一个帧(Frame),每次进入一个方法的时候就会有一个新的方法帧压入线程执行堆栈,可以通过StackFrame获取相关的信息,比如当前代码所在文 ...
- eval(str)函数
转载:地址于http://blog.csdn.net/SeeTheWorld518/article/details/47983511 eval(str)函数很强大,官方解释为:将字符串str当成有效的 ...
- JSTL(使用if-else实现分页)
一.认识标签 <c:choose>标签没有任何属性. <c:when>标签具有以下列出的一个属性. <c:otherwise>标签没有任何属性. <c:whe ...
- InceptionV3代码解析
InceptionV3代码解析 参考博文:https://blog.csdn.net/superman_xxx/article/details/65451916 读了Google的GoogleNet以 ...
- 在vue中使用pug
安装pug npm i pug pug-loader pug-cli pug-filters -D pug :安装pug pug-loader:pug的loader pug-cli:pug 编译工具 ...
- 子类A继承父类B, A a = new A(); 则父类B构造函数、父类B静态代码块、父类B非静态代码块、子类A构造函数、子类A静态代码块、子类A非静态代码块 执行的先后顺序是
按照先后顺序: 1,静态先于非静态代码库执行(静态代码块随着类的加载而加载,初始化只执行一次) 2,父类先于子类 3,非静态代码块优于构造函数执行 所以执行顺序如下: 父类B静态代码块->子类A ...
- 微信小程序slider应用,可加减的slider控制
<view class="control-w "> <block wx:for="{{controls}}" wx:key="id& ...