这两个高阶函数定义有什么区别吗?
主要的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; }
首先,函数指针很难。 考虑到你可以将一个函数作为parameter passing给另一个函数,需要一些类似于理解recursion的思维弯曲。 一开始你就不会明白,但是突然之间,就像你脑海中的理解大闸门,你开悟了。
但是,你仍然需要知道在C和C ++中传递函数的规则作为参数。 在这些语言中,function并不是一stream的公民,因此对你可以用它们做什么有很多限制。
句法
函数指针语法有点难看。 基本解剖学是[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
因此,将它们作为函数parameter passing的唯一方法是传递它们的地址而不是复制它们。 (这是数组的争议,但这就是它的工作原理。)这些“值”被转换成指针作为parameter passing的事实称为“衰减”。
这两个原型是兼容的(即它们指的是相同的function,而不是不同的过载),因此两者之间没有区别:
int foo(void bar()); int foo(void (*bar)());
除了视觉之外,这两个声明之间绝对没有区别。 这两个函数都会接受一个函数指针 ,不论它看起来是不是这样,因为衰减 。 然而,由于衰变往往被认为是一个令人讨厌和令人困惑的事情,所以大多数开发人员更愿意明确地要求函数指针(许多开发人员甚至不知道函数types会衰减)。
隐式转换
现在,关于传递函数作为参数。 这只是一个衰减的结果:函数必须隐式转换为它们的函数指针types。 这意味着你可以在需要函数指针的地方传递一个函数,编译器会为你提供地址。 为此,这些都是一样的:
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
结合这两个解释,你会发现你的四个函数调用都是一样的。 apply1
和apply2
都接受相同types的参数( int (*)(void)
),即使对于apply1
来说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运算符,因为指针types和引用types之间没有隐式转换。 击败衰败通常被认为是一件好事,因为衰退往往是令人困惑的。
int baz(); Bar(baz); // valid Bar(&baz); // INVALID
函数引用遵循与正常引用相同的规则:它们只能在定义时分配,不能为空。
types定义
你可以使用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
“返回”表示的types。 对于这个构造, function
和&function
是有区别的:
int bar(); decltype(bar); // type is int () decltype(&bar); // type is int (*)()
如果您想将types作为模板parameter passing给std::unique_ptr
,则此差异尤为重要。
std::unique_ptr<void, decltype(free)> foo; // INVALID std::unique_ptr<void, decltype(&free)> foo; // valid
第一个是无效的,因为它会尝试创build一个函数作为unique_ptr
的实例字段。