我们知道看起来像void()
的参数将被重写为void(*)()
。这与int[]
变为int*
的数组到指针衰减类似。在许多情况下,使用数组会将其衰减为指针。是否存在函数“衰减”的参数以外的情况?
C ++标准规定:
§8.3.5/ 5
...确定每个参数的类型后,任何参数 类型“T的数组”或“函数返回T”被调整为“指针 to T“或”指向函数返回T的指针,分别为......
由于下面的评论者似乎并不相信我......这就是我的编译器所显示的内容。
void handler(void func())
{
func(42);
}
main.cpp: In function 'void handler(void (*)())':
main.cpp:5:12: error: too many arguments to function
func(42);
^
答案 0 :(得分:9)
有三种转换被认为是左值转换:左值到右值,数组到指针和函数到指针。你可以称之为"衰变"因为std::decay
将对这些类型做什么,但标准只是将其称为函数到指针的转换[conv.func]:
函数类型
T
的左值可以转换为类型为“T
指针”的prvalue。结果是指向函数的指针。
如果您在进行函数到指针转换时询问的是什么情况,它们基本上与其他两个左值转换发生时相同。如果我们按顺序完成标准,以下是功能到指针转换发生的详尽列表:
使用函数作为操作数,[expr] / 9:
每当glvalue表达式作为操作符的操作数出现时,该操作符需要该操作数的prvalue, 应用左值到右值(4.1),数组到指针(4.2)或函数到指针(4.3)标准转换 将表达式转换为prvalue。
使用函数作为varargs函数的参数,[expr.call] / 7:
当给定参数没有参数时,参数的传递方式是接收函数可以通过调用
va_arg
(18.10)来获取参数的值...左值 - 到 - 对参数表达式执行rvalue(4.1),数组到指针(4.2)和函数到指针(4.3)标准转换。
你可以static_cast
离开这个转换,[expr.static.cast] / 7:
任何标准转换序列(第4条)的反转,不包含左值到右值(4.1),arrayto- 指针(4.2),函数指针(4.3),空指针(4.10),空成员指针(4.11)或布尔值(4.12) 转换可以使用
static_cast
显式执行。
尽管不然,你传入的操作数将被转换,[expr.static.cast] / 8:
左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)转换应用于 操作数。
使用reinterpret_cast
,[expr.reinterpret.cast] / 1:
表达式
reinterpret_cast<T>(v)
的结果是将表达式v
转换为类型的结果T
。如果T
是左值引用类型或函数类型的右值引用,则结果为左值;如果T
是 rvalue引用对象类型,结果是xvalue;否则,结果是prvalue和lvalue-torvalue (4.1),数组到指针(4.2)和函数到指针(4.3)标准转换是在 表达v
。
使用const_cast
,[expr.const.cast],与上面的措辞基本相同。使用条件运算符[expr.cond]:
执行Lvalue-to-rvalue(4.1),array-to-pointer(4.2)和函数到指针(4.3)标准转换 关于第二和第三个操作数。
请注意,在上述所有情况下,它总是所有左值变换。
在模板中也会发生函数到指针的转换。将函数作为非类型参数传递,[temp.arg.nontype] /5.4:
对于函数指针类型的非类型模板参数,函数到指针的转换(4.3) 应用
或者输入演绎,[temp.deduct.call] / 2:
如果
P
不是参考类型:
- - 如果
A
是数组类型,则数组到指针标准转换(4.2)生成的指针类型为 用于代替A
进行类型扣除;否则,- - 如果
A
是函数类型,则函数到指针标准转换生成的指针类型(4.3) 用于代替A
进行类型扣除;否则,
或转换函数模板扣除,大致相同的措辞。
最后,当然,在{meta.trans.other]中定义的std::decay
本身强调我的:
让
U
成为remove_reference_t<T>
。如果is_array<U>::value
为真,那么 成员typedef类型应等于remove_extent_t<U>*
。 如果is_function<U>::value
为真,则成员typedef类型应相等add_pointer_t<U>
即可。否则成员typedef类型等于remove_cv_t<U>
。 [注意:此行为类似于 当左值表达式用作右值时,应用左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)转换,但也剥离 cv 来自类类型的限定符,以便更接近地模拟按值 论证传递。 - 后注]
答案 1 :(得分:4)
当涉及数据类型时,函数不是C和C ++中的一等公民(问题是关于C ++,但行为是从C继承的)。它们是代码,而不是数据,它们不能被复制,作为参数传递给函数或由函数返回(但所有这些都可能发生在指向函数的指针上。)
这就是为什么函数名被视为指向该函数的指针而不是函数体本身。
使用函数(名称)而不是指向函数的指针的可能性只是语言对程序员的礼貌,而不是“衰变”。
数组相同:它们不被复制,不作为函数参数传递,也不由函数返回。而是使用它们的第一个元素的地址(复制,作为函数参数传递或由函数返回)。这就是为什么可以使用数组名称而不是第一个元素的地址,而这又是一种编写更少(和更少混淆)代码的方法。
对于编译器,函数是一块存储器(包含要在某个时间执行的代码),它不移动并由其地址识别,即指向函数的指针。数组也是一个数据块 不移动并由其地址(也是其第一个元素的地址)标识。同样,这是一个指针。
编译器将更高级别(C,C ++)的function
和array
的概念转换为较低级别(汇编程序,机器代码)所理解的原始值(指针)。
答案 2 :(得分:0)
数组和函数之间的另一个明显相似之处如下:
void bar(string message)
{
cout << message << endl;
}
void main()
{
int myArray[10];
int* p = myArray; //array to pointer to array
void (*f)(string);
f = bar; //function to function pointer decay
}
答案 3 :(得分:-1)
是的,它们都是指针,第一个是函数指针,第二个是指向一个int的指针。 *看起来像一个点。