std::function
in C++11 and 14 does not have the behavior you desire.
It also fails to SFINAE detect bad overloads.
We can wrap it in a different type that both has the behavior you desire (void
discarding return) and has SFINAE bad overload detection while we are at it as follows:
template<class Sig>
struct checked_function;
template<class R, class... Args>
struct checked_function<R(Args...)>:std::function<R(Args...)> {
using function = std::function<R(Args...)>;
checked_function(std::nullptr_t):function() {}
checked_function():function() {}
template<class F, class=typename std::enable_if<
std::is_convertible<
typename std::result_of< F(Args...) >::type
, R
>::value
>::type>
checked_function( F&& f ):function( std::forward<F>(f) ) {}
template<class F, class=typename std::enable_if<
std::is_convertible<
typename std::result_of< F(Args...) >::type
, R
>::value
>::type>
checked_function& operator=( F&& f ) { return function::operator=( std::forward<F>(f) ); }
checked_function& operator=( checked_function const& o ) = default;
checked_function& operator=( checked_function && o ) = default;
checked_function( checked_function const& o ) = default;
checked_function( checked_function && o ) = default;
};
template<class... Args>
struct checked_function<void(Args...)>:std::function<void(Args...)> {
using function = std::function<void(Args...)>;
checked_function(std::nullptr_t):function() {}
checked_function():function() {}
template<class F, class=typename std::enable_if<
std::is_same<
typename std::result_of< F(Args...) >::type
, void
>::value
>::type>
checked_function( F&& f, int*unused=nullptr ):function( std::forward<F>(f) ) {}
template<class F>
static auto wrap(F&& f){
return [f_=std::forward<F>(f)](auto&&...args){
f_( std::forward<decltype(args)>(args)... );
};
}
template<class F, class=typename std::enable_if<
!std::is_same<
typename std::result_of< F(Args...) >::type
, void
>::value
>::type>
checked_function( F&& f, void*unused=nullptr ):
function( wrap(std::forward<F>(f)) ) {}
template<class F>
typename std::enable_if<
!std::is_same<
typename std::result_of< F(Args...) >::type
, void
>::value,
checked_function&
>::type operator=( F&& f ) { return function::operator=( wrap(std::forward<F>(f)) ); }
template<class F>
typename std::enable_if<
std::is_same<
typename std::result_of< F(Args...) >::type
, void
>::value,
checked_function&
>::type operator=( F&& f ) { return function::operator=( std::forward<F>(f) ); }
checked_function& operator=( checked_function const& o ) = default;
checked_function& operator=( checked_function && o ) = default;
checked_function( checked_function const& o ) = default;
checked_function( checked_function && o ) = default;
};
It now compiles in C++14 (not in C++11, due to wrap
: wrap
can be replaced at point of call with a copy of its own body, so...). Could probably reduce boilerplate by a bunch.
It uses some C++14 features (move-into-lambda to be precise in wrap
-- you can do away with that by adding more boilerplate).
Not run yet.