31

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

#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

4 回答 4

17

函数和函数引用(即这些类​​型的id 表达式)几乎立即衰减为函数指针,因此在您的情况下,表达式funcf_ref实际上成为函数指针。你也可以打电话(***func)(5)(******f_ref)(6)如果你喜欢。

如果您希望&-operator 像已应用于函数本身一样工作,则最好使用函数引用,例如&func,与 相同&f_ref,但又&f_ptr是别的东西。

于 2013-10-05T17:22:31.203 回答
14

“为什么函数指针不需要取消引用?”

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

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

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

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

因此,当您定义参考时:

void (& f_ref)(int) = func;

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

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

工作方式与直接使用完全相同func

func(1);
(*func)(4);
于 2013-10-05T17:37:47.523 回答
4

这里

address-of 运算符的作用与您期望的一样,因为它指向一个函数但不能被分配。函数在用作右值时会转换为函数指针,这意味着您可以多次取消引用函数指针并取回相同的函数指针。

于 2013-10-05T17:34:26.610 回答
1

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

但似乎有类型错误。该消息implicit conversion from void*是非常特定于此代码的编译器。我猜你正在使用 GCC/Clang。GCC/Clang 提供了获取标签地址的能力,例如&&label. 结果值的类型为void*。其他编译器会输出类似cannot take address of temporaryor的东西invalid 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;
};

但谁会用同样的名字命名一切呢?

于 2019-05-12T11:22:50.590 回答