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?
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);
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With