Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any differences between these two higher-order function definitions?

Tags:

c++

c

Are there any differences among 4 statements in main? I feel 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; } 
like image 942
flee Avatar asked Sep 05 '13 00:09

flee


People also ask

What is the definitions of higher-order functions?

A higher order function is a function that either takes a function as an argument or returns a function. This type of function has implementations in many programming languages including Go, JavaScript, Python, etc; and they tend to be a question used during interviews.

What is a higher order function give any two examples?

Let's look at an example of a higher order functionconst numbers = [1, 2, 3, 4, 5]; function addOne(array) { for (let i = 0; i < array. length; i++) { console. log(array[i] + 1); } } addOne(numbers); The function addOne() accepts an array, adds one to each number in the array, and displays it in the console.

What is a higher order function and what are their advantages?

2.1 Higher Order Functions. Funs encourages us to encapsulate common patterns of design into functional forms called higher order functions. These functions not only shortens programs, but also produce clearer programs because the intended meaning of the program is explicitly rather than implicitly stated.

Why are higher-order functions important?

Higher order functions can help solve many problems. With that said, with the many useful benefits that higher order functions can bring to the table like code size reduction and re-usability, you will decrease the likelihood of stressing over people around you when used that way.


1 Answers

First off, function pointers are hard. Thinking that you can pass a function as a parameter to another function requires some mind-bending similar to understanding recursion. You won't get it at first, but then all of a sudden it's like the floodgates of understanding open in your brain and you're enlightened.

But then, you still have to know the rules of passing functions as parameters in C and C++. In these languages, functions are not first-class citizens, so there are a lot of 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 parentheses around *name are necessary to disambiguate between a function pointer and a function returning a 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 about the same as arrays. When passed, they change into a pointer. Compare:

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 non-assignable and non-copyable:

int foo[4]; int bar[4] = foo; // invalid  int foo(); int bar() = foo; // invalid 

Therefore, the only way to pass them as function parameters is to pass their address instead of copying them. (This is disputable for arrays, but that's how it works.) The fact that these "values" are transformed into pointers when passed as parameters is called "decay".

These two prototypes are compatible (that is, they refer to the same function, not different overloads), and therefore, there is no difference between the two:

int foo(void bar()); int foo(void (*bar)()); 

Visuals aside, there is absolutely no difference between those two declarations. Both functions accept a function pointer, whether it looks like it or not, because of decay. Though, since decay is often considered a nasty and confusing thing, most developers will prefer to explicitly ask for a function pointer (and a lot of developers don't even know function types can decay).

Implicit Conversions

Now, about passing functions as parameters. This one is simply a consequence of decay: functions have to be implicitly convertible 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, these are, once again, the same:

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 those two explanations, and you'll realize that your four function calls are all the same. apply1 and apply2 both accept the same type of parameter (int (*)(void)), even if it's not obvious for apply1; and when you call the functions with func instead of &func, the compiler implicitly takes the address for you and makes it equivalent to &func.


The following is outside the scope of the question, but it elaborates on the previous part, and I think it's kind of neat.

Function References [C++ only]

It is a little-known fact, but it is also possible to pass references to arrays and functions: in this case, no decay happens. 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 scenario, you are not allowed to use the address-of operator, because there is no implicit conversion between pointer types and reference types. Defeating decay is generally seen as a good thing, as decay is often confusing.

int baz(); Bar(baz); // valid Bar(&baz); // INVALID 

Function references obey the same rules as normal references: they can be assigned only at definition time, and cannot be null.

Typedefs

You can make function pointers less ugly using typedef.

typedef int (*X)(); X func; // func is a pointer to a function that returns an int 

Things get more interesting if you take out 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; is equivalent to a declaration saying int func();. Don't do this at home, unless you want to confuse the hell out of everyone.

decltype makes a difference [C++ only]

Another interesting difference between functions and function pointers arises with the use of decltype. decltype "returns" the type of an expresson. 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 the 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 invalid because it would attempt to create a function as an instance field of unique_ptr.

like image 127
zneak Avatar answered Oct 11 '22 19:10

zneak