函数指针与函数引用

时间:2013-10-05 17:18:08

标签: c++ function pointers reference function-pointers

在下面的代码中,函数指针和我认为的“函数引用”似乎具有相同的语义:

#include <iostream>
using std::cout;

void func(int a) {
    cout << "Hello" << a << '\n';
}
void func2(int a) {
    cout << "Hi" << a << '\n';
}

int main() {
    void (& f_ref)(int) = func;
    void (* f_ptr)(int) = func;

    // what i expected to be, and is, correct:
    f_ref(1);
    (*f_ptr)(2);

    // what i expected to be, and is not, wrong:
    (*f_ref)(4); // i even added more stars here like (****f_ref)(4)
    f_ptr(3);    // everything just works!

    // all 4 statements above works just fine

    // the only difference i found, as one would expect:
//  f_ref = func2; // ERROR: read-only reference
    f_ptr = func2; // works fine!
    f_ptr(5);

    return 0;
}

我在Fedora / Linux中使用了gcc版本4.7.2

更新

我的问题是:

  1. 为什么函数指针不需要解除引用?
  2. 为什么解除引用函数引用不会导致错误?
  3. 是否有任何情况我必须使用一个而不是另一个?
  4. 为什么f_ptr = &func;有效?因为func应该衰减为指针?
    虽然f_ptr = &&func;不起作用(隐式转换自void *

4 个答案:

答案 0 :(得分:13)

函数和函数引用(即那些类型的 id-expressions )几乎立即衰减到函数指针中,因此表达式funcf_ref实际上成为你的函数指针案件。如果您愿意,也可以致电(***func)(5)(******f_ref)(6)

在您希望& - 运算符像应用于函数本身一样工作的情况下,最好使用函数引用,例如&func&f_ref相同,但&f_ptr是其他内容。

答案 1 :(得分:13)

“为什么函数指针不需要解除引用?”

因为函数标识符本身实际上是指向函数的指针:

  

4.3功能指针转换
  §1函数类型T的左值可以转换为“指向T的指针”的右值。结果是指向函数的指针。

“为什么取消引用函数引用不会导致错误?”

基本上,您可以将引用定义为定义别名(替代名称)。即使在 8.3.2 References 中的标准中,在部分寻址中创建对象的引用,您会发现:
“引用可以被视为对象的名称。”

所以当你定义一个引用时:

void (& f_ref)(int) = func;

它使您能够在可能使用f_ref的任何地方使用func,这就是为什么:

f_ref(1);
(*f_ref)(4);

与直接使用func的方式完全相同:

func(1);
(*func)(4);

答案 2 :(得分:2)

请参阅here

地址运算符的行为与您期望的一样,因为它指向一个函数但无法分配。当用作rvalues时,函数将转换为函数指针,这意味着您可以多次取消引用函数指针并返回相同的函数指针。

答案 3 :(得分:0)

由于这里的其他人给出了很好的答案,因此没有任何答案可以解释为什么f_ptr = &&func;不起作用。将运算符&的地址应用于变量/函数时,将获得其地址。地址本身是一个r值/一个临时变量。您不能使用临时地址。

但是似乎存在类型错误。消息implicit conversion from void*专门针对此代码进行编译。我猜您正在使用GCC / Clang。 GCC / Clang提供了获取&&label之类的标签地址的功能。结果值的类型为void*。其他编译器将输出类似cannot take address of temporaryinvalid syntax的内容。在使用这些编译器时,在特殊情况下可能会隐藏此类错误而没有任何警告:

int main() {
    int foo = 42;
    foo:;
    void* a = &foo; // take the address of a variable/function
    void* b = &&foo; // take the address of a label

    std::cout << *(int*)a << '\n';
    goto *b;
};

但是谁会给所有相同的名字命名?