结构
指针
1声明函数指针
//原型
int f(int a);
//函数指针
int (*p)(in a)
int (*p)(int )
2函数指针调用函数
//两种
int (*p)(int a){
return a;
}
//1
(*p)(5);
//2
p(5);
3没有你想的那么简单
const int *(*p)(const int * ,int)=f1
const double *(*p[])(cinst double *,int)={f1,f2,f3}
上面这个式子就比较蛋疼了 他是一个数组 里面有三个指向函数的指针,这也许不麻烦,但是如果我们想把这些指针赋值给某些变量是 这就有点头大了,但是 我们可以用c++11中可以用auto自动推断类型
auto p1=p[0];
auto p2=&p;
typedef int a
typedef constdouble *(*p)(const int *)
p p1=f1;
默认参数
void f(int a=0;int b=1;int c=1;)
函数返回const的引用
const int & f(int a){
int b=a;
return b;
}
f(a,b)=3;
函数模板
?顾名思义就是为函数创建同一的模板
声明
template< typename AnyType>
void f(AnyType & a,AnyType b){
}
第一行指出 要创建一个模板,并将命名为AnyType,关键词template和typename是必需的,class 可以代替typename,可以用class 是因为向前兼容
注意:模板函数只是源代码阶段 ,在编译阶段编译器根据具体出来的模板类型 来定义独立的函数定义,就像手工定义这些函数一样,最终代码是不包含任何模板的
局限性
在函数模板中 typename更像是一种文本的替换
void f( T a,T b){
}
对于上面的模板中T 如果表示的是int ,double 的数据类型 a>b 是可以通过的,但是如果是数组和结构类型的还 a>b 是不可以通过的,但是有问题就有解决的办法
显示具体化(第三代显示具体化)
显示声明一种模板
void f(int a,int b); //普通的函数
template<class T> //普通的模板
void f(T a,T b);
template<> void f<int >(int ,int ) //普通显示具体化,不带变量名
template<> void f<int >(int a ,int b ) //普通显示具体化,带变量名
template <>void f(int ,int ) //因为编译器可以根据后面的具体类型来判断,所以可以取代哦<int>
显示实例化
上面说了模板并不是创建具体的函数实例,而是编译器根据具体的参数类型创建具体的函数代码,但是有一种方法就是直接创建函数实例,那就是显示实例化
template void f<int>(int ,int )
警告 上面的<int>是不能少的 且同一个文件(转换单元中使用同一类型的显示实例化和现实具体化将出错)
还可以在程序中使用函数来创建显示实例化
template <typename T>
T add(T a,T b){
return a+b;
}
int a=0;
double b=0;
cout<<add<double>(a,b)<<endl;
这里面 模板与函数调用 add(a,b)是不匹配的,因为参数不同,但是通过add<double>(a,b)可以强制转换为double类型的
重载模板函数
void f(T & ,T&)
void f(T[] &,T[] &,int)//模板函数中也是可以有普通类型的
上面就是重载模板函数(其实还有复杂的情况,不过一般在编程中不会有人故意把模板写的很复杂的,因为这除了装逼没什么用),重载模板函数和默认参数之间有可能二义要注意
编译器选择哪个函数版本呢?
如今 我们讲了 函数重载,函数模板,函数模板的重载,c++找不到一模一样的原型时是不会停下脚步的,那么c++有一个什么样的策略来解决找“妈妈”的问题呢?
我妈把这个过程成为重载解析
第一步 创建候选函数列表,其中包含与被调用函数相同的函数和函数模板
第二步 使用候选函数列表创建可行函数列表,这些都是参数数目正确的函数,为此有一个隐式的转换序列,其中包括实参类型与相应的形参类型完全匹配的情况
第三步 确定是否有最佳的可行函数,如果有,则用它否则该函数条用出错
当然上面的话一般人是不能理解的,举个栗子
f('B')
void f(int); //#1
flotf(flot,flot=3); //#2
void f(char) //#3
char * f(const char *) //#4
char f(const chat *) //#5
template <class T>void f(const T &) //#6
template <class T>void f(T *) //#7
对于 上面的f('B')而言我们只考虑特征标 而不考虑返回类型 其中#4和#7不可行,因为整形类型不能被隐式转换为指针类型,剩余的一个模板可用来生成具体化,其中T被替换为chat类型,这样剩下5个可行的函数,其中每一个函数,如果它是声明的唯一一个函数,都可以被使用
接下来编译器必须确定哪一个是最佳的,从最佳到最差的顺序如下
1完全匹配 ,但常规函数优先于模板
2提升转换 (例如 char和shorts 自动转换为int,float自动转换为double)
3标准转换(例如,int转换为char,long转换为double)
4用户定义的转换,如类声明中定义的转换
例如,函数#1优于#2,因为char到int转换为提升转换,而char到float的转换时标准转换。函数#3,函数#5,函数#6都优与#1,#2,因为都是完全匹配,#3,#5优#6,因为#6函数是模板,这种分析引出两个问题,什么是完全匹配, 如果两个函数(#3和#5)都是完全匹配,将如何办?,通常,有两个函数完全匹配时一种错误,但这一规则有两个例外。显然我们需要对这一点做更深入的探讨
1完全匹配和最佳匹配
在进行完全匹配时,c++允许做一些“无关紧要的转换”
从实参 | 到形参 |
Type | Type & |
Type & | Type |
Type(argument-list) | Type(*)(argument-list) |
Type | const Type |
Type | volatile Type |
Type * | const Type |
Type * | volatile Type * |
Type [] | * Type其中 |
其中上面Type为任意类型 Type(argument-list)意味着用作实参的函数名与用作形参的函数指针只要返回类型和参数列表相同
知道上面列表之后我们就知道了,多个匹配的原型时是否出现二义性
void f( int) //#1
void f(const int) //#2
void f(int &) /#3
void f(const int &) //#4
上面的例子应该会出现二义性 但是有时候即使两个函数完全匹配,但是仍可以完成重载解析,首先,指向非const数据的指针和引用优先与非const指针和引用参数匹配,在上面的例子中如果只定义#3和#4都是完全匹配,则选择#3 ,这个规则值只出现在指针和引用而普通的数据还是会出现二义性。
一个完全匹配优先与另一个情况是,其中一个是模板,而另一个不是,这种情况下非模板函数将优先与模板函数
如果两个都是模板函数,这较具体的模板函数优先,例如显示具体化优先于模板隐式生成的具体化
其中“最具体化”是指编译器那种类型执行的转换最小
template <class T>void f(T t)
template<class T>void f(T *t)
如果f(& a) 实参是一个地址会这么样呢 因为第二个具体化为指针 所以我们所第二个最具体
关键字decltype
在c++的模板中我们会遇到这样的问题
template<class T1,calss T2 >
void f(T1 a,T2 b){
decltype(x+y) xy=x+y;
........
}
像上面那样 x+y的结果你不知道是什么类型 的所以我们用decltype
声明
<pre class="cpp" name="code"><span style="font-family:Arial;background-color: transparent;"></span>decltype(x) y; //第一种声明
decltype(x+y) xy; //声明和初始化分开
xy=x+y;
decltype (x+y) xy=x+y;//声明和初始化一起
然后 decltype 比上面的事例要复杂,编译器必须便利一个核对表例如decltype(expression) var
则核对表如下
第一步 如果expression 是一个没有括号括起来的标识符 则var 的类型与该标识符的类型相同,包括const
第二步 如果expression是一个函数调用则var 和返回值相同(编译器不会调用函数 只是根据原型 来确定返回类型)
第三步 如果expression是带括号的标识符,则是var的引用
double x=4.4;
decltype ((x)) xx=x; //xx double &
decltype(x) xxx=x; //xxx double
第四部 如果如果前面的条件都不满足 则var的类型月expression相同
需要多次声明可结合使用typedef和decltype
typedef decltype (x+y) xy;
c++后置返回类型
template<class T1,calss T2 >
Type? f(T1 a,T2 b){
return x+y;
}
无法预知x+y的值什么什么类型的 如果设置decltype(x+y)但是此时还没有声明x和y,他们不再作用于内,必须声明参数后使用decltype
有一种就技术就是c++后置返回类型
auto f(T x ,T y)->decltype(x+y)
就是我们之前自动声明类型,其中auto起始就是一个占位符,他告诉编译器真正的类型在后面