函数到函数指针"衰减"

时间:2014-10-25 06:03:40

标签: c++

我们知道看起来像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);
        ^

4 个答案:

答案 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 ++)的functionarray的概念转换为较低级别(汇编程序,机器代码)所理解的原始值(指针)。

答案 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的指针。 *看起来像一个点。