I need to extract the type of a function object parameter.
Lambdas get translated into a closure object with the operator(). std::function has got the operator(), too.
So, I can get a pointer to the operator() to pass to another function, in this way:
template <typename F, typename T, typename R, typename ... Args>
void helper(F&, R (T::*)(Args...) const)
{
// do something with Args types
}
template <typename F>
void bar(F f)
{
helper(f, &F::operator());
}
void freefunc(int) {}
void foo()
{
// lambda: ok
bar([](int){});
// std::function: ok
const std::function<void(double)> f = [](double){};
bar(f);
// std::bind: does not compile
auto g = std::bind(freefunc, std::placeholders::_1);
bar(g);
}
std::bind should create an object with the operator(), too. However, my code does not work with std::bind(), and I cannot understand why.
gcc produces this error:
In instantiation of 'void bar(F) [with F = std::_Bind<void (*(std::_Placeholder<1>))(int)>]':
<source>:58:8: required from here
<source>:47:11: error: no matching function for call to 'helper(std::_Bind<void (*(std::_Placeholder<1>))(int)>&, <unresolved overloaded function type>)'
47 | helper(f, &F::operator());
| ~~~~~~^~~~~~~~~~~~~~~~~~~
<source>:39:6: note: candidate: 'template<class F, class T, class R, class ... Args> void helper(F&, R (T::*)(Args ...) const)'
39 | void helper(F&, R (T::*)(Args...) const)
| ^~~~~~
<source>:39:6: note: template argument deduction/substitution failed:
<source>:47:11: note: couldn't deduce template parameter 'T'
47 | helper(f, &F::operator());
| ~~~~~~^~~~~~~~~~~~~~~~~~~
ASM generation compiler returned: 1
<source>: In instantiation of 'void bar(F) [with F = std::_Bind<void (*(std::_Placeholder<1>))(int)>]':
<source>:58:8: required from here
<source>:47:11: error: no matching function for call to 'helper(std::_Bind<void (*(std::_Placeholder<1>))(int)>&, <unresolved overloaded function type>)'
47 | helper(f, &F::operator());
| ~~~~~~^~~~~~~~~~~~~~~~~~~
<source>:39:6: note: candidate: 'template<class F, class T, class R, class ... Args> void helper(F&, R (T::*)(Args ...) const)'
39 | void helper(F&, R (T::*)(Args...) const)
| ^~~~~~
<source>:39:6: note: template argument deduction/substitution failed:
<source>:47:11: note: couldn't deduce template parameter 'T'
47 | helper(f, &F::operator());
What's the correct way to do the same with std::bind?
CodePudding user response:
What you want to do is unfortunately impossible because the return type of std::bind is too loosely specified by the standard.
std::function::operator()is clearly defined by the standard, so you can match it againstR (T::*)(Args... ), see [func.wrap.func.general],- for lambda functions, it's not that clear from [expr.prim.lambda.closure#3], but I'd say that it should work,
- for
std::bind, the specification [func.bind.bind#4] is much broader because it only says that you can callg(u1, u2, …, uM)wheregis the returned value fromstd::bind, so there is no guarantee that the return type ofstd::bindeven has anoperator()member-function.
The actual implementation problem here, which is the same for gcc, clang and msvc, is that the operator() member-function of the return value is actually a template, so you cannot use &F::operator() directly — you cannot take the address of a templated (member-)function.
CodePudding user response:
In short - you cannot do this for std::bind or at least it cannot be guaranteed. Even though result of std::bind is closure-like it is quite different. Consider what would happen if something along the lines of class below was result of std::bind:
class bind_result {
template<class T, class U>
U operator()(const T&);
private:
// implementation details
};
Indeed, your function g would not work for class like that, since the operator() is a template.
