模板基础知识1——《C++程序设计语言(第四版)》第23章 模板 笔记
来源:互联网 发布:jacs在什么数据库 编辑:程序博客网 时间:2024/06/02 06:18
基础知识
- 模板实例化是从一个模板和一组模板实参来生成代码,模板实例化生成的代码与程序员手工扩展模板得到的代码完全一样。
- 模板机制最大的弱点是无法直接表达对模板实参的要求。
- 对于一个模板使用不同模板实参生成的类型是不同类型,例如,假定Circle是一种Shape:
vector<Shape> vs{vector<Circle>{}}; //错误vector<Shape*> vs{vector<Circle*>{}}; //错## 标题 ##误
类模板成员
模板类可以有几种不同类型的成员
数据成员
非static数据成员可以在定义时初始化,也可在构造函数中初始化
template<typename T>struct X { int m1 = 7; T m2; X(const T &x):m2{x}{}};X<int> xi{9};X<string> xs{"123"}
非static数据成员可以是const的,但却不能是constexpr的。
成员函数
非static成员函数的定义可以在类模板内部,也可在外部,如:
template<typename T>struct X{ void mf1(){/*...*/} //类内定义 void mf2();};template<typename T>void X<T>::mf2(){/*...*/} //类外定义
类模板成员函数可以是virtual的,也可以不是。但是,一个虚成员函数名不能再用作一个成员函数模板名。
成员类型别名
模板参数名T只能被模板自身访问,如果其他代码想使用元素类型,可提供一个别名,如:
template<typename T>class Vector {public: using value_type = T; using iterator = Vector_iter<T>; Vector_iter是在别处定义的}
static成员
一个类外定义的static数据或函数成员在整个程序中只能有唯一一个定义。如:
template<typename T>struct X { static constexpr Point p {100, 250}; //Point必须是一个字面值常量类型 static const int m1 = 7; static int m2 = 8; //错误:不是const static int m3; static void f1(){/*...*/} static void f2();};template<typename T> int X<T>::m1 = 88; //错误:有两个初始化器template<typename T> int X<T>::m3 = 99;template<typename T> void X<T>::f2(){/*...*/}
一个static成员只有真被使用时才需要定义,如:
template<typename T>struct X { static int a; static int b;};int *p = &X<int>::a;
此时编译器会报告X::a“未定义”,而对X::b就不会。
成员类型
template<typename T>struct X { enum E1{a, b}; enum E2; //错误:基础类型未知 enum class E3; enum E4 :char; struct C1 {/*...*/}; struct C2;};template<typename T>enum class X<T>::E3 {a, b}; //必须的template<typename T>enum class X<T>::E4:char {x, y}; //必须的template<typename T>struct X<T>::C2 {/*...*/}; //必须的
成员模板
template<typename Scalar>class complex { Scalar re, im;public: complex():re{}, im{}{} //默认构造函数 template<typename T> complex(T rr, T ii = 0) : re{rr}, im{ii}{} complex(const complex&) = default; template<typename T> complex(const complex<T>& c) : re{c.real()}, im{c.imag()}{} //...};
这种定义允许数学上有意义的复数类型转换,同时禁止不合需要的窄化转换
complex<float> cf; //默认值complex<double> cd{cf}; //正确,float向double转换complex<float> cf2{cd}; //错误,double向float转换complex<float> cf2{2.0, 3.0}; //错误,窄化转换禁止complex<double> cd2{2.0F, 3.0F}; //正确
- 成员模板不能是virtual的。如果C++允许这样的代码,用于实现虚函数机制的传统虚函数表技术就无法使用了。
- 在模板中尽量避免嵌入类型,除非他们真正依赖于所有模板参数,否则会引起代码膨胀
友元
Matrix和Vector相乘的例子
template<typename T> class Matrix;template<typename T>class Vector { T v[4];public: friend Vector operator*<>(const Matrix<T>&, const Vector&); //...};template<typename T>class Matrix{ Vector<T> v[4];public: friend Vector operator*<>(const Matrix<T>&, const Vector&); //...};
友元函数后面的<>是必须的,它清楚地指出友元函数是一个模板函数。若没有<>,友元函数将被假定是非模板函数。
template<typename T>Vector<T> operator*(const Matrix<T> &m, const Vector<T> &v){ Vector<T> r; return r;}
函数模板
函数模板的声明和定义可在不同位置
函数模板实参
如果不能从函数实参推断出一个模板实参,就必须显示指定它,如:
template<typename T>T* create(); //创建一个T,返回指向它的指针void f(){ int *p = create<int>(); //函数模板,实参为int int *q = create(); //错误:无法推断模板实参}
函数模板实参推断
template<typename T>class Xref {public: Xref(int i, T *p) //保存一个指针:Xref是所有者 :index{i}, elem{p}, owner{true} {} Xref(int i, T& r) //保存一个指向r的指针,所有者是其他人 :index{i}, elem{&r}, owner{false} {} Xref(int i, T &&r) //将r移入Xref,Xref变为所有者 :index{i}, elem{new T{move(r)}}, owner{true} {} ~Xref() { if(owned) delete elem; } //...private: int index; T *elem; bool owned;};string x{"There and back again"};Xref<string> r1{7, "Here"}; //调用Xref(int i, T &&r)Xref<string> r2{9, x} //调用Xref(int i, T& r)Xref<string> r3{3, new string{"There"}}; //调用Xref(int i, T *p)template<typename T> T&& std::forward(typename remove_reference<T>::type &t) noexcept;template<typename T> T&& std::forward(typename remove_reference<T>::type &&t) noexcept;template<typename TT, typename A>unique_ptr<TT> make_unique(int i, A&& a){ return unique_ptr<TT>{new TT{i, forward<A>(a)}};}
函数模板重载
template<typename T> T sqrt<T>;template<typename T> complex<T> sqrt(complex<T>);double sqrt(double);void f(complex<double> z){ sqrt(2); //sqrt<int>(int) sqrt(2.0); //sqrt(double) sqrt(z): //sqrt<double>(complex<double>)}
- 找到参与重载解析的所有函数模板特例化版本
- 如果两个函数模板都可以调用,且其中一个比另一个更特殊化,则接下的步骤只考虑最特殊化的版本
- 对前两个步骤还留在候选集中的函数和所有候选普通函数一起进行重载解析
- 如果一个普通函数和一个特例化版本匹配得一样好,那么优先选择普通函数,方法与普通函数重载解析相同
- 如果没有发现任何匹配,则调用错误;如果得到多个一样好的匹配,则调用有二义性,也是一个错误
可以通过显式限定来消除二义性,如
template<typename T>T max(T, T)void f(){ max<int>('a', 1); max<double>(2.7, 4);}
实参代入失败
template<typename Iter>typename Iter::value_type mean(Iter first, Iter last); //1号template<typename T>T mean(T*, T*); //2号void f(vector<int> &v, int *p, int n){ auto x = mean(v.begin(), v.end); //正确:调用1号 auto y = mean(p, p + n); //正确:调用2号}
如果我们未给出2号mean(),则编译器会为mean(p, p + n)调用1号版本,我们就会得到一个实例化错误
重载和派生
重载解析规则保证函数模板能完美第和继承机制结合使用
template<typename T> class B {/*...*/};template<typename T> class D: public B<T> {/*...*/};template<typename T> void f(B<T>*);void g(B<int> *pb, D<int> *pd){ f(pb); f(pd); //f<int>(static_cast<B<int>*>(pd))}
重载和非推断的参数
template<typename T, typename C>T get_nth(C &p, int n);struct Index { operator int(); //...};void f(vector<int> &v, short s, Index i){ int i1 = get_nth<int>(v, 2); //严格匹配 int i2 = get_nth<int>(v, s); //short到int的标准类型转换 int i3 = get_nth<int>(v, i); //用户自定义类型转换: Index到int}
模板别名
template<typename T, typename Allocator = allocator<T>> vector;using Cvec = vector<char>;template<int>struct int_exact_traits{ using type = int;};template<>struct int_exact_traits<8>{ using type = char;};template<>struct int_exact_traits<16>{ using type = short;};template<int N>using int_exact = typename int_exact_traits<N>::type;int_exact<8> a = 7;
源码组织
主要有两种
1. 在一个编译单元中,使用模板前包含其定义。
2. 在一个编译单元中,使用模板前只包含其声明。在编译单元中稍后的位置包含模板定义(可能在使用之后)。
3. (不支持)在一个编译单元中,使用模板前只包含其声明。在其他编译单元定义模板。
阅读全文
0 0
- 模板基础知识1——《C++程序设计语言(第四版)》第23章 模板 笔记
- 模板基础知识4——《C++程序设计语言(第四版)》第26章 实例化 笔记
- 模板基础知识3——《C++程序设计语言(第四版)》第25章 特例化 笔记
- 模板基础知识2——《C++程序设计语言(第四版)》第24章 泛型算法 笔记
- C程序设计语言——第2章学习笔记
- C++{}初始化——《C++程序设计语言第四版》
- C++程序设计语言--第十三章:模板
- C++ Template学习笔记之函数模板(1)——函数模板定义
- C++ Template学习笔记之函数模板(1)——函数模板定义
- 《C程序设计语言》笔记----第四章 函数与程序结构
- C程序设计语言(K&R)第四章学习笔记
- 《C++ primer(第四版)》读书笔记7-第16章 模板与泛型编程
- 函数的模板1——基础知识
- C程序设计语言(第2版)摘要
- C程序设计语言(第2版)
- c程序设计语言笔记1
- C程序设计语言笔记1
- [学习笔记—Objective-C]《Objective-C 程序设计 第6版》第四章 数据类型和表达式
- maven学习之从搭建maven环境到在项目中如何使用maven
- Hadoop2.0中HDFS高可用性的实现原理
- shiro安全框架HelloWord入门篇
- JDK8堆内存划分变化
- 严重: Exception sending context initialized event to listener instance of class [org.springframework.w
- 模板基础知识1——《C++程序设计语言(第四版)》第23章 模板 笔记
- leetcode 547. Friend Circles 寻找图的环的数量 + 深度优先遍历DFS
- 什么是OGNL表达式
- The number of divisors(约数) about Humble Numbers
- URI is not registered ( Setting | Project Settings | Schemas and DTDs )
- 机房重构
- 机房收费系统——登录
- Biorhythms 笔记
- 基础知识之nginx重写规则