std::function<>
is a useful wrapper around almost any callable thing, including free functions, lambdas, functors, member functions, results from st
The general case cannot work. There are specific cases (that support C++11 lambdas but not C++14, does not support bind
, supports non-overloaded functions and methods, does not support function objects) where you can build a make_function
that "works". There are also some functions you can write that are useful.
The make_function
that "works" is usually a bad idea.
Just keep a copy of the original function object around if you don't need to convert it to a std::function>
. You only need to convert it to a std::function>
when you already know the types you are going to be passing to it, and what you are doing with the return type -- ie, when you are type-erasing around the signature.
std::function
is not a "all purpose holder for a function type". It is a type-erasure class that is used to erase type information so you can have code that operates uniformly on it. If you are deducing the signature from the object, there is little if no reason to store it in a std::function
at all.
There are narrow cases where it is useful, when you want to behave differently based on the input and output argument types of the function argument you are passed. In this case, your signature deduction facility could be useful: tying it to std::function
would be a bad idea, in my opinion, as it ties together the two independent concepts (signature deduction and type erasure) in a way that is rarely useful.
In short, reconsider.
Now, I mentioned above that there are some useful utilities that could be called make_function
. Here are two of them:
template
std::function< std::result_of_t< F&(Args...) >
make_function( F&& f ) {
return std::forward(f);
}
but requires that you list the arguments. It deduces return value.
This variant:
template
struct make_function_helper {
F f;
template
std::result_of_t< (F&&)(Args...) >
operator()(Args&&...args)&& {
return std::forward(f)(std::forward(args)...);
}
template
std::result_of_t< (F const&)(Args...) >
operator()(Args&&...args) const& {
return f(std::forward(args)...);
}
template
std::result_of_t< (F&)(Args...) >
operator()(Args&&...args) & {
return f(std::forward(args)...);
}
template(Args...) ), R>{}
>>
operator std::function()&&{ return std::forward(f); }
template(Args...) ), R>{}
>>
operator std::function()const&{ return f; }
};
template
make_function_helper make_function(F&&f) { return {std::forward(f)}; }
doesn't actually make a function, but lets you call a function with multiple std::function
overloads and pick between them correctly. It is also invokable in a perfect-forwarding way back to the underlying F
. In 97/100 cases, you won't be able to notice the difference between this make_function
and one that returns an actual std::function
(those last cases being cases where someone expects to type-deduce the std::function
from the type, and perfect forwarding failures)
So:
int foo(std::function< int(int) >) { return 0; }
int foo(std::function< void() >) { return 1; }
int main() {
std::cout << foo( []{} ) << "\n";
}
fails to compile, while
int main() {
std::cout << foo( make_function([]{}) ) << "\n";
}
succeeds. However, even this trick is just patching a hole in the design of std::function
which I hope will be rolled into the post-concepts std::function
. At that point, you may have well just store the original object.
In general, you cannot determine a single guaranteed unique signature for a callable object x or a function name x.
In the case of a callable object, the operator()
could have multiple overloads. This can be done in C++14 with [](auto x)
lambdas, and with function objects or the return from std::bind
in C++03.
With the name of a function (or function pointer), the name does not correspond to a single object (or pointer). Resolution is done when it is passed to the std::function
, and the correct overload is often picked (because std::function
takes a R(*)(Args...)
pointer, and maybe something similar for member functions (I cannot recall)).
Doing so with a make_function
is nearly impossible.