Function pointers and return type conversions

Suppose I have a function that performs some side effect and then returns an answer:

int foo() { perform_some_side_effect(); return 42; } 

I want to bind foo to a function pointer, but I'm not interested in the answer, just a side effect:

 void (*bar)() = foo; 

However, it looks like an error like:

 error: invalid conversion from 'int (*)()' to 'void (*)()' 

What is the reason for this error? Why doesn't the type system allow me to ignore the answer?


On a side note, this works if I wrap the function pointer in std::function :

 std::function<void()> baz = foo; 

How does std::function (apparently) manage to get around this restriction in the type system?

+5
source share
4 answers

What is the reason for this error? Why doesn't the type system allow me to ignore the answer?

The reason is that the types are different, and the generated code at the place of the call (via the function pointer) is different. Consider a calling convention in which all arguments are written onto the stack, and the space for the return value is also stored on the stack. If the call passes through void (*)() , then the place for the return value will not be reserved on the stack, but the function (without realizing how it is called) will still write 42 to the place where the calling reserved space should be.

How does std :: function (apparently) manage to get around this restriction in the type system?

This is not true. It creates a function object that completes the call to the actual function. It will contain a member like:

 void operator()() const { foo(); } 

Now that the compiler processes the call to foo , it knows what it needs to do to call the function that returns int , and will do so in accordance with the calling convention. Since the template is not returned, it simply ignores the value that was actually returned.

+9
source

std::function should only be compatible with the source code, that is, it can generate a new class that generates new calendar code that ignores the result. The function pointer must be binary and cannot perform this task - void(*)() and int(*)() points to the same code.

+1
source

You can come up with std::function<> to do this for your specific case:

 void __func_void() { foo(); } 

This is actually a bit more complicated, but the fact is that it generates the template code along with type-erasing so as not to worry about the specifics.

+1
source

In addition to what others have said, the caller also needs a return type to find out which destructor he should call on the result (the return value may be temporary).


Unfortunately, this is not as easy as

 auto (*bar)() = foo; 

Although GCC and Clang accept this. I need to double-check the specification to make sure that it is really correct.

Update: Specification Specified

An automatic type specifier means that the type of the declared variable must be inferred from its initializer or that the function declarator must include the return type of the return type.

This may be confusing for fast reading, but it is implemented by GCC and clang to apply only to the toplevel declarator. In our case, this is a pointer-pointer. A declaration nested within it is a function declaration. So just replace auto with void , and then the compiler will infer the type for you.


By the way, you can always do this work manually, but it requires several deceivers to work.

 template<typename FunctionType> struct Params; template<typename ...Params> struct Params<void(Params...)> { template<typename T> using Identity = T; template<typename R> static Identity<R(Params...)> *get(R f(Params...)) { return f; } }; // now it easy auto bar = Params<void()>::get(foo); 
+1
source

Source: https://habr.com/ru/post/974036/


All Articles