auto callback(std::future<int>) -> void
is the declaration of a entity of type void(std::future<int>)
called callback
. When listed as an argument, the compiler adjusts this to be a pointer-to-function of type void(*)(std::future<int>)
.
Your lambda is stateless, and as such can be implicitly converted to a function pointer.
Once you add a non-trivial capture, your code will stop compiling:
[argc](std::future<int> number) {
std::cout << argc << '
';
...
Now, ignoring your question content and looking at the title...
There is a modest cost for a std::function
because it is a value-type, not a view-type. As a value-type, it actually copies its argument.
You can get around this by wrapping the calling object in a std::ref
, but if you want to state "I won't keep this function object around longer than this call", you can write a function_view
type as follows:
template<class Sig>
struct function_view;
template<class R, class...Args>
struct function_view<R(Args...)> {
void* ptr = nullptr;
R(*pf)(void*, Args...) = nullptr;
template<class F>
using pF = decltype(std::addressof( std::declval<F&>() ));
template<class F>
void bind_to( F& f ) {
ptr = (void*)std::addressof(f);
pf = [](void* ptr, Args... args)->R{
return (*(pF<F>)ptr)(std::forward<Args>(args)...);
};
}
// when binding to a function pointer
// even a not identical one, check for
// null. In addition, we can remove a
// layer of indirection and store the function
// pointer directly in the `void*`.
template<class R_in, class...Args_in>
void bind_to( R_in(*f)(Args_in...) ) {
using F = decltype(f);
if (!f) return bind_to(nullptr);
ptr = (void*)f;
pf = [](void* ptr, Args... args)->R{
return (F(ptr))(std::forward<Args>(args)...);
};
}
// binding to nothing:
void bind_to( std::nullptr_t ) {
ptr = nullptr;
pf = nullptr;
}
explicit operator bool()const{return pf;}
function_view()=default;
function_view(function_view const&)=default;
function_view& operator=(function_view const&)=default;
template<class F,
std::enable_if_t< !std::is_same<function_view, std::decay_t<F>>{}, int > =0,
std::enable_if_t< std::is_convertible< std::result_of_t< F&(Args...) >, R >{}, int> = 0
>
function_view( F&& f ) {
bind_to(f); // not forward
}
function_view( std::nullptr_t ) {}
R operator()(Args...args) const {
return pf(ptr, std::forward<Args>(args)...);
}
};
live example.
This is also useful in that it is a strictly simpler kind of type erasure than std::function
, so it could be educational to go over it.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…