c++ - 这两个高阶函数定义之间有什么区别吗?

标签 c++ c

main 中的 4 个语句之间有什么区别吗?
我觉得只有 apply2(&func) 才有意义。但是,所有 4 个都返回相同的值。

int func(void) 
{
    return 1;
}

int apply1( int f1(void) )
{
    return f1();
}

int apply2( int (*f1) (void) ) 
{
    return f1();
}

int main() 
{
    apply1(func); 
    apply1(&func);
    apply2(func);
    apply2(&func);

    return 0;
}

最佳答案

首先,函数指针很难。认为您可以将一个函数作为参数传递给另一个函数需要一些类似于理解递归的思维方式。一开始你不会明白,但突然之间,它就像在你的大脑中打开了理解的闸门,你就开悟了。

但是,您仍然必须了解在 C 和 C++ 中将函数作为参数传递的规则。在这些语言中,函数不是一等公民,所以你可以用它们做什么有很多限制。

句法

函数指针语法有点难看。基本解剖结构是[return type] (*[name])([argument list]) . *name周围的括号是 必需消除函数指针和返回指针的函数之间的歧义:

// not function pointers: * not grouped to function name
int x(); // function that returns an int
int* x(); // function that returns an int*
int *x(); // also a function that returns an int*, spaces don't matter

// function pointers: * grouped to function name
int (*x)(); // pointer to a function that returns an int
int* (*x)(); // pointer to a function that returns an int*

衰变

在作为参数传递方面,函数的行为与数组大致相同。当通过时,它们会变成一个指针。相比:
void Foo(int bar[4]); // equivalent to: void Foo(int* bar)
void Bar(int baz()); // equivalent to: void Bar(int (*baz)())

这仅仅是因为函数和数组是不可赋值和不可复制的:
int foo[4];
int bar[4] = foo; // invalid

int foo();
int bar() = foo; // invalid

因此,将它们作为函数参数传递的唯一方法是传递它们的地址而不是复制它们。 (这对于数组是有争议的,但这就是它的工作原理。)当作为参数传递时,这些“值”被转换为指针的事实被称为“衰减”。

这两个原型(prototype)是兼容的(即它们指的是同一个函数,而不是不同的重载),因此,两者之间没有区别:
int foo(void bar());
int foo(void (*bar)());

除了视觉效果,这两个声明之间绝对没有区别。由于衰减,两个函数都接受一个函数指针,无论它看起来像与否。尽管如此,由于衰减通常被认为是一件令人讨厌和令人困惑的事情,因此大多数开发人员更愿意明确要求函数指针(而且许多开发人员甚至不知道函数类型可以衰减)。

隐式转换

现在,关于将函数作为参数传递。这只是衰减的结果:函数必须隐式转换为其函数指针类型。这意味着你可以在需要函数指针的地方传递一个函数,编译器会为你获取它的地址。为此,它们再次相同:
int foo();
int (*bar)() = foo; // the compiler implicitly assigns the address of foo to bar
int (*baz)() = &foo; // you explicitly assign the address of foo to baz

结合这两种解释,你会发现你的四个函数调用都是一样的。 apply1apply2两者都接受相同类型的参数( int (*)(void) ),即使对于 apply1 来说并不明显;当您使用 func 调用函数时而不是 &func ,编译器会为您隐式地获取地址并使其等效于 &func .

以下超出了问题的范围,但它详细说明了前一部分,我认为它有点整洁。

函数引用 [仅限 C++]

这是一个鲜为人知的事实,但也可以传递对数组和函数的引用:在这种情况下,不会发生衰减。像这样:
void Foo(int (&bar)[4]); // NOT equivalent to void Foo(int* bar)
void Bar(int (&baz)()); // NOT equivalent to void Bar(int (*baz)())

在这种情况下,不允许使用 address-of 运算符,因为指针类型和引用类型之间没有隐式转换。战胜衰败通常被视为一件好事,因为衰败往往令人困惑。
int baz();
Bar(baz); // valid
Bar(&baz); // INVALID

函数引用遵循与普通引用相同的规则:它们只能在定义时赋值,不能为空。

类型定义

您可以使用 typedef 使函数指针不那么难看.
typedef int (*X)();
X func; // func is a pointer to a function that returns an int

如果你去掉 (*),事情会变得更有趣。部分:
typedef int X();
X* func; // func is a function pointer
X& func; // func is a function reference [C++ only]
X func; // func is a function declaration (!!)

在后一种情况下,X func;相当于声明 int func(); .不要在家里这样做,除非你想把每个人都搞糊涂。
decltype有所作为 [仅限 C++]

函数和函数指针之间另一个有趣的区别出现在 decltype 的使用中。 . decltype “返回”表达式的类型。对于此构造,function 之间存在差异。和 &function :
int bar();
decltype(bar); // type is int ()
decltype(&bar); // type is int (*)()

如果您想将类型作为模板参数传递给 std::unique_ptr,这种差异尤其重要。 .
std::unique_ptr<void, decltype(free)> foo; // INVALID
std::unique_ptr<void, decltype(&free)> foo; // valid

第一个无效,因为它会尝试创建一个函数作为 unique_ptr 的实例字段。 .

关于c++ - 这两个高阶函数定义之间有什么区别吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18626080/

相关文章:

C++ for_each 调用回调函数 vector 并向每个回调函数传递一个参数

c - 程序说明

c - 如何找到领先的数字

c - C语言中如何获取父对象?

c - 函数声明与原型(prototype)的替代 (K&R) C 语法

c - main 中变量地址与函数中变量地址的区别

c++ - 当顺序很关键时初始化 C++ 组件的最佳实践?

c++ - 尝试销毁 sf::Font 时出现段错误

c++ - RcppExport 错误原因

c++ - CMake Generator for Visual Studio Linux 跨平台