Are there differences between the two definitions of higher order functions?

Are there differences between 4 statements basically? I feel that only apply2 (& func) makes sense. However, all 4 return the same value.

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; } 
+45
c ++ c
Sep 05 '13 at 0:50
source share
1 answer

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:

 // 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* 

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]); // equivalent to: void Foo(int* bar) void Bar(int baz()); // equivalent to: void Bar(int (*baz)()) 

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; // the compiler implicitly assigns the address of foo to bar int (*baz)() = &foo; // you explicitly assign the address of foo to baz 

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]); // NOT equivalent to void Foo(int* bar) void Bar(int (&baz)()); // NOT equivalent to void Bar(int (*baz)()) 

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); // valid Bar(&baz); // INVALID 

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; // func is a pointer to a function that returns an int 

Everything becomes more interesting if you print the part (*) :

 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 (!!) 

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); // type is int () decltype(&bar); // type is int (*)() 

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; // INVALID std::unique_ptr<void, decltype(&free)> foo; // valid 

The first is not valid because it will try to create the function as an instance field of unique_ptr .

+70
Sep 05 '13 at 0:55
source share



All Articles