模板和STL 1

模板和STL 1

模板起源

==》{针对具体类型的实现
借助参数宏摆脱类型的限制
让预编译器写代码}

模板起源
针对具体类型的实现
C/C++语言的静态类型系统,在满足效率与安全性要求的同时,很大程度上也成为阻碍程序员编写通用代码的桎浩。它迫使人们不得不为每一种数据类型编写完全或几乎完全相同的实现,虽然他们在抽象层面上是一致的。
– int max_int(int x, int y){
return x>y ? x:y;
}

–double max_double(double x,double y ){
return x>y ?x:y;
}

–string max_string(string x, string y){
return x>y ?x:y;
}

借助参数宏摆脱类型的限制
.宏定义只是在预处理器的作用下,针对源代码的文本替换,其本身并不具备函数语义。因此借助于参数宏(又名宏函数)可以在某种程度上使程序的编写者摆脱那些源于类型的约束和限制,但同时也因此丧失了类型的安全性
–#define max(x,y)((x)>(y)?(x):(y))
–cout << max(123,456)<<endl;
–cout << max(1.23,4.56)<<endl;
–string a = “hello”,b = “world”;
cout << max(a,b) << endl;
–cout << max(“hello”,“world”)<<endl; //错误

让预编译器写代码
.利用宏定义构建通用代码的框架,让预处理器将其扩展为针对不同类型的具体版本。将宏的一般性和函数的类型安全性完美地结合起来。
– #define MAX(T) T max_##T (T x,T y){
return x>y ?x:y}
–MAX(int)
MAX(double)
MAX(string)
–#define max(T) max_##T
–cout << max(int) (123,456)<<endl;
cout << max(string)(“hell0”,“world”)<<endl;

函数模板{ 函数模板的定义 函数模板的使用 函数模板的隐式推断 函数模板的重载}
函数模板的定义{ 函数模板的语法形式 类型参数}
函数模板的使用{ 模板实例化 使用函数模板时才实例化 二次编译}
函数模板的隐式推断{根据调用参数推断模板参数 不能隐式推断的三种情况 隐式推断与缺省值之间的矛盾}
函数模板的重载{普通函数和函数模板构成重载 函数模板不支持隐式类型转换 显示指示空模板参数列表 保证模板参数与调用参数一致 函数模板内优先选择普通函数}

函数模板的定义
模板参数的语法形式
模板参数必须用如下形式的语法来声明
–template <typename 类型参数1,typename 类型参数2,…>
例如
–template <typename A ,typename b,typename _C>
A function(b arg){
_C var;

}

类型参数
.可以使用任何标识符作为类型参数的名称,但使用"T"已经成为了一种惯例。类型参数"T"表示的是,调用者调用这个函数时所指定的任意类型
.可以使用任何类型(基本类型、类类型等)实例化模板的类型参数,前提是所使用的类型必须能够满足该模板所需要的操作。比如用一个不支持"<"运算符的类型,实例化max模板的类型参数,将引发编译错误
–template
T const& max(T const& x,T const & y){
return x<y ?y:x;
}

函数模板的使用
模板的实例化
通常而言,并不是把模板编译成一个可以处理任何类型的单一实体,而是对于实例化模板参数的每种类型,都从模板产生出不同的实体。这种用具体类型代替模板参数的过程叫做实例化。它产生了一个模板的实例
–template T const& max{
T const$ x,T const& y}{return x<y ?y:x;}
–int const& max(int const& x,int const& y){
return x<y ? y:x;}

–double const& max(double const& x,double const& y){
return x<y ?y:x;}
–string const& max(string const& x,string const& y){
return x<y?y:x;}

使用函数模板时才实例化
只要使用函数模板,编译器就会自动引发这样一个实例化过程,因此程序员并不需要额外地请求对模板实例化
–函数模板名<类型实参1,类型实参2,…>(调用实参表);
例如
– ::max (123,456);
– ::max (1.23,4.56);
– ::max(“hello”,“world”);
.函数模板的类型实参表放在尖括号中,调用实参表放在圆括号中,且类型实参表必须位于函数模板名和调用实参表之间
.有时候函数模板的类型实参表或调用实参表可以为空,但尖括号和圆括号不能不写

二次编译:
每个函数模板事实上都被编译了两次
–一次是在实例化之前,先检查模板代码本身,查看语法是否正确
–另一次是在实例化期间,结合所使用的类型参数,再次检查模板代码,查看是否所有的调用都有效;
但是请注意,只有在第二次编译时,才会真正产生二进制形式的机器指令;
作为第一次编译的结果,仅仅在编译器内部形成的一个用于描述该函数模板的数据结构,即所谓模板的内部表示;

函数模板的隐式推断
根据调用参数推断模板参数
如果函数模板调用参数的类型相关于该模板的模板参数,那么在调用该函数模板时,即使不显示指定模板参数,编译器也有能力根据调用参数的类型隐式推断出正确的模板参数,以获得与普通函数调用一致的语法表达
-template
void foo(T const& x,T const& y){
cout << “T=”<< typeid(T).name()<<endl;}
–foo(123,456);//T=i
-double x=1.23,y=4.56;
foo(x,y);//T=d
–foo(“hello”,“world”); //T=A6_c
foo(“hello”,“terena”);//错误

不能隐式推断的三种情况
以下三种情况,不能隐式推断,必须显示指定模板参数
–不是全部模板参数都与调用参数的类型相关
.template<typename A,typename V>void foo(A arg){… V var…}
.foo(123);//错误
foo<int,string>(123);
-隐式推断的同时不允许隐式类型转换
.templatevoid foo(T x,T y){…}
.foo(123,4.56);//错误
foo(123,4.56);
foo((double)123,4.56);//显示类型转换可以隐式推断

  • 返回类型不能隐式推断
    .template<typename A,typename R>R foo(A arg){…}
    .int ret =foo(1.23);//错误
    int ret=foo<double,int>(1.23);

隐式推断与缺省值之间的矛盾
.函数模板参数的隐式推断与缺省值之间存在矛盾,因此函数模板的模板参数不能带有缺省值
–template
T const& max(T const& x,T const& y){
return x<y?y:x;
}
-cout << ::max(1.23,4.56)<<endl; //错误
.C++ 2011标准允许函数模板带有缺省模板参数,但依然会优先选择隐式推断的结果
-g++ … -std=c++0x // <GCC 4.8
g++ …-std=c++11 //>GCC 4.8
-cout << ::max(1.23,4.56)<<endl;//4.56

函数模板的重载
普通函数和函数模板构成重载
.普通函数和可实例化为该函数的函数模板构成重载关系。在其他条件都相同的情况下,编译器优先选择普通函数,除非函数模板能够产生具有更好匹配性的函数实例
-char const* const& max(char const* const& x,char const* const& y){…}//1
template
T const& max(T const& xd,T const& y){…}//2
-char const* x=“ABC”;
char const* y =“AB”;
cout << ::max(x,y)<<endl;//1
cout<< ::max(100,200)<<endl;//2

函数模板不支持隐式类型转换
函数模板的隐式实例化不支持隐式类型转换,但普通函数支持。因此在参数传递过程中如需隐式类型转换,则编译器将优先选择普通函数
-char const* const& max(char const* const& x,char const* const& y){…}//1
template
T const& max(T const& X,T const& y){…}//2

–char const* x=“ABC”;
char* y =“AB”;
cout << ::max(x,y)<<endl;//1

显示指示空模板参数列表
可以显示指定一个空的模板参数列表,明确告知编译器使用函数模板,但模板参数却由隐式推断决定。即便是在函数模板中选择,编译器也会尽可能选择类型约束性强的版本,即便是在函数模板中选择,编译器也会尽可能选择类型约束性强的版本,即更特殊的版本
-char const* const& max(char const* const& x,char const* const& y){…}//1
template
T const& max(T const& x,T const& y){…}//2
template
T* const& max(T* const& x,T* const& y){…}//3
-char const* x=“AB”;
char const* y=“ABC”;
cout<< ::max<>(x,y)<<endl; //3

保证模板参数与调用参数一致
如果为函数模板显示指定了模板参数,那么所选择的重载版本必须保证模板参数与调用参数的类型相一致
-template
T const& max(T const& x,T const& y){…}//1
template
T* const& max(T* const& x,T* const& y){…}//2
–char const* x=“ABC”;
char const* y=“AB”;
cout<<::max<char const*>(x,y)<<endl;//1

函数模板内优先选择普通函数
.即使是在函数模板的实例化函数中,编译器仍然坚持普通函数优先原则,前提是该普通函数在一次编译时可见
– char const* const& max(char const* const& x,char const* const& y){…}//1
template
T const& max(T const& x,T const& y){…}//2
template
T const& max(T const& x,T const& y,T const& z){return ::max(::max(x,y),z);}//3
–char const* x=“ABC”;
char const* y=“AB”;
char const* z=“A”;
cout<<::max(x,y,z)<<endl;//3==>1

类模板
类模板的定义{模板参数的语法形式,类型参数}

模板参数的语法形式:
模板参数必须用如下形式的语法来声明
–template <typename 类型参数1,typename 类型参数2,…>
例如:
–template<typename A,typename b,typename _C>
class MyClass{
public:
A m_a;
b foo(_C c);
};

类型参数
在类模板的内部,类型参数可以像其他任何具体类型一样,用于成员变量、成员函数、成员类型(内部类型),甚至基类的声明
–template<typename M,typename R,typename A,typename V,typename T ,typename B>
class MyClass:public B{
M m_mem;
R function(A arg){…V var…}
typedef T* pointer;
};

发布了21 篇原创文章 · 获赞 16 · 访问量 3767

猜你喜欢

转载自blog.csdn.net/qq_40632341/article/details/104140173