First, function pointers are difficult. Thinking that you can pass a function as a parameter to another function, you need some twist of the mind, similar to understanding recursion. At first you won’t get it, but then all of a sudden it, like flows of understanding, opens in your brain and you are enlightened.
But then you still need to know the rules for passing functions as parameters in C and C ++. Functions in these languages are not first-class citizens, so there are many restrictions on what you can do with them.
Syntax
The function pointer syntax is a little ugly. The basic anatomy is [return type] (*[name])([argument list]) . The braces around *name needed to remove the interference between the function pointer and the function returning the pointer:
Decay
In terms of passing as parameters, functions behave in much the same way as arrays. When they are transmitted, they change to a pointer. For comparison:
void Foo(int bar[4]);
This is simply because functions and arrays are not assigned or copied:
int foo[4]; int bar[4] = foo; // invalid int foo(); int bar() = foo; // invalid
Therefore, the only way to pass them as functional parameters is to pass their address, rather than copy them. (This is debatable for arrays, but how it works.) The fact that these “values” turn into pointers when passed as parameters is called “decay”.
These two prototypes are compatible (that is, they relate to the same function, and not to different overloads), and therefore there is no difference between them:
int foo(void bar()); int foo(void (*bar)());
Visualization aside, there is absolutely no difference between the two ads. Both functions take a pointer to a function, regardless of whether it looks like this or not due to decay. Although, since decay is often considered unpleasant and confusing, most developers prefer to explicitly request a pointer to a function (and many developers do not even know that types of functions can decay).
Implicit conversions
Now about passing functions as parameters. This is simply a consequence of decay: functions must be implicitly converted to their function pointer type. This means that you can pass a function where a function pointer is expected, and the compiler will get its address for you. For this purpose they match again:
int foo(); int (*bar)() = foo;
Combine these two explanations and you will realize that your four function calls are the same. apply1 and apply2 both take the same parameter ( int (*)(void) ), even if this is not obvious to apply1 ; and when you call functions using func instead of &func , the compiler implicitly takes the address for you and makes it equivalent to &func .
The following is beyond the scope of the question, but it details the previous part, and I think it is a little neat.
Function Links [C ++ Only]
This is a little-known fact, but you can also pass references to arrays and functions: in this case, decay does not occur. Like this:
void Foo(int (&bar)[4]);
In this case, you are not allowed to use the operator address, because there is no implicit conversion between pointer types and reference types. Defeat decay is usually considered a good thing, since decay is often confused.
int baz(); Bar(baz);
Function references follow the same rules as regular references: they can only be assigned at the time of definition and cannot be null.
Type definitions
You can make function pointers less ugly by using typedef .
typedef int (*X)(); X func;
Everything becomes more interesting if you print the part (*) :
typedef int X(); X* func;
In the latter case, X func; equivalent to a statement expressing int func(); . Do not do this at home unless you want to confuse the hell of everyone.
decltype matters [C ++ only]
Another interesting difference between functions and function pointers arises using decltype . decltype "returns" the type of expression. For this construct, there is a difference between function and &function :
int bar(); decltype(bar);
This difference is especially important if you want to pass a type as a template parameter, say, to std::unique_ptr .
std::unique_ptr<void, decltype(free)> foo;
The first is not valid because it will try to create the function as an instance field of unique_ptr .