是否存在从函数指针到函数的隐式转换?

Aik*_*iko 0 c++ pointers function language-lawyer

我写这篇文章主要是为了澄清我在 Stackoverflow 上偶然发现的一些关于函数指针的令人困惑/误导的信息。

让我们从一个例子开始:

#include <iostream>

void func ()
{
    std::cout<<"func here"<<'\n';
}

int main()
{
    void (*fp)()=func;
    void (&fref)()=func;

    func();//call through function
    (&func)();//call through function pointer
    (*fp)();//call through function
    fp();//call through function pointer
    fref();//call through function
    (&fref)();//call through function pointer
}
Run Code Online (Sandbox Code Playgroud)

这打印:

func here
func here
func here
func here
func here
func here
Run Code Online (Sandbox Code Playgroud)

可以看出,由于函数到函数指针的衰减cppreference,大多数时候可以使用函数来代替函数指针。

函数类型 T 的左值可以隐式转换为指向该函数的纯右值指针。这不适用于非静态成员函数,因为不存在引用非静态成员函数的左值。

但除此之外,函数指针看起来也可以用来代替函数,因为我可以使用它来调用函数而无需显式取消引用。

此外,这个 Stackoverflow 答案

另请注意,您不需要使用一元 * 来通过函数指针进行调用;两者 (*p1_foo)(); 和 (p1_foo)(); 由于函数到函数指针的转换,具有相同的结果。

以及这个 Stackoverflow 的答案

还有一个双重的便利:调用位置的函数指针会自动转换为函数值,因此您不必编写 * 来通过函数指针进行调用。

让它看起来好像存在一个隐式函数指针来进行函数转换。

Aik*_*iko 5

\n

不存在从函数指针到函数的隐式转换。

\n

自 ISO 国际标准 ISO/IEC 14882:2020(E) \xe2\x80\x93 编程语言 C++ 起,没有提及此类转换。

\n
\n

但除此之外,函数指针看起来也可以用来代替函数,因为我可以使用它来调用函数而无需显式取消引用。

\n
\n

这可能就是为什么一些 SO 答案(甚至一些不太知名的 C++ 书籍!)得出错误的结论,即函数指针本质上与函数相同,并使用函数到函数指针的衰减作为证据。然而,这种隐式转换只能以一种方式起作用!这意味着第一个答案的引用部分是不正确的。

\n

为什么函数调用仍然成功?

\n

我们可以使用函数指针调用函数而无需显式解引用的原因实际上在于内置函数调用运算符“()”的工作方式cppreference

\n
\n

命名函数的表达式可以是\na) 引用函数的左值表达式\nb) 指向函数的指针\nc) 选择成员函数的显式类成员访问表达式\nd) 隐式类成员访问表达式,例如成员函数名称在另一个成员函数中使用。

\n
\n

啊哈!因此函数调用运算符可以直接将函数指针作为表达式。没有隐式转换。如果您阅读第二个 SO 答案的引用,它明确提到函数指针需要位于使用函数调用运算符调用的调用位置。

\n

这也意味着在函数调用之外的所有上下文中,确实需要在需要函数的地方取消引用函数指针,例如初始化函数引用时:

\n
void func ()\n{\n    std::cout<<"func here"<<\'\\n\';\n}\n\nint main()\n{\n    void (*fp)()=func;//OK (implicit function to function pointer decay)\n\n    void (&&fref1)()=&func;//error: invalid initialization of reference of type \'void (&&)()\' from expression of type \'void (*)()\'\n\n    void (&fref2)()=*fp;//OK\n\n    void (&fref3)()=fp;// error: invalid initialization of reference of type \'void (&)()\' from expression of type \'void (*)()\'\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n