Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it illegal to invoke a std::function<void(Args...)> under the standard?

All quotes are from N3797.

4/3 [conv]

An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t

This implies no expression can be implicitly converted to void, as void t=e is illegal for all expressions e. This is even true if e is an expression of type void, such as void(3).

So an expression of type void cannot be implicitly converted to void.

Which leads us to:

20.9.2/2 Requirements [func.require]

Define INVOKE (f, t1, t2, ..., tN, R) as INVOKE (f, t1, t2, ..., tN) implicitly converted to R .

In short, INVOKE(f, t1, t2, ..., tN, R) is never valid when R is void, as nothing (including void) can be implicitly converted to void.

As a result of this, all std::function<void(Args...)> have the property !*this and thus cannot be called, as the only constructors that do not have !*this as a postcondition (or do not copy such state from another function of the same type) require Callable of one of the parameters.

20.9.11.2/7 Class template function [func.wrap.func]

Requires: F shall be CopyConstructible . f shall be Callable ( 20.9.11.2 ) for argument types ArgTypes and return type R . The copy constructor and destructor of A shall not throw exceptions.

20.9.11.2/2 Class template function [func.wrap.func]

A callable object f of type F is Callable for argument types ArgTypes and return type R if the expres- sion INVOKE (f, declval()..., R) , considered as an unevaluated operand (Clause 5 ), is well formed ( 20.9.2 ).

As demonstrated above, there are no Callable expressions for std::function<void(Args...)>.

If somehow such a std::function<void(Args...)> where found, invoking operator() would be ill formed:

invocation [func.wrap.func.inv]

Effects: INVOKE (f, std::forward(args)..., R) ( 20.9.2 ), where f is the target ob- ject ( 20.9.1 ) of *this .

as INVOKE(f, std::forward<ArgTypes>(args)..., void) is ill formed for all arguments and f.

Is this line of reasoning sound?

like image 424
Yakk - Adam Nevraumont Avatar asked Aug 20 '14 01:08

Yakk - Adam Nevraumont


People also ask

What is the purpose of std :: Invoke?

The std::invoke function in the C++ standard library is usually used to call a functor with parameters. std::function<void(int)> func = ...; // same as func(42) std::invoke(func, 42);

What is STD invoke?

std::invoke( f, args... ) is a slight generalization of typing f(args...) that also handles a few additional cases. Something callable includes a function pointer or reference, a member function pointer, an object with an operator() , or a pointer to member data.

Can std :: function be moved?

Yes, unless you catch it, or unless you mark the destructor noexcept(false) . And in the latter case you have to be pretty careful or it may result in std::terminate() anyway.

Why did we need to use an std :: function object?

It lets you store function pointers, lambdas, or classes with operator() . It will do conversion of compatible types (so std::function<double(double)> will take int(int) callable things) but that is secondary to its primary purpose.


1 Answers

Yes, your analysis is correct; I came to the same conclusion here.

According to Daniel Kruegler, this issue should appear on the library defect list subsequent to the next mailing:

A corresponding library issue has already been submitted, but is not yet visible in the issue list.

Hopefully once that becomes visible we'll also have a conclusive answer to whether it is allowable to construct a std::function with signature returning void passing a callable with signature returning non-void (Using `std::function<void(...)>` to call non-void function).


Update: this was entered as LWG 2420, which was resolved in favor of special-casing void return type to static_cast the result of the invoked function to void. This means that a callable returning non-void can be the target of a std::function<void(...)>. LWG2420 was applied as a post-publication correction to C++14; meanwhile, all compilers I'm aware of effectively apply this behavior as an extension in C++11 mode.

like image 82
ecatmur Avatar answered Sep 20 '22 09:09

ecatmur