首先我先介绍一下2个要点:
a:使用嵌套 std::bind 时,首先评估内部 std::bind,并且在评估外部 std::bind 时返回值将替换其位置。这意味着std::bind(f, std::bind(g, _1))(x)
执行方式与f(g(x))
做。如果外部 std::bind 需要函子而不是返回值,则内部 std::bind 应该被 std::ref 包装。
b:使用 std::bind 无法将右值引用正确转发到函数。还有reason https://stackoverflow.com/a/4871563/2195556已经详细说明了。
那么,我们来看看这个问题。这里最重要的函数可能是 func_wrapper ,它旨在执行 3 个目的:
- 首先完美转发一个函子到 doit 函数模板,
- 然后使用 std::bind 将 doit 作为闭包,
- 最后让 func 函数模板执行 std::bind 返回的函子。
根据b点,目的1无法实现。因此,让我们忘记完美转发,doit 函数模板必须接受左值引用参数。
根据a点,目的2将通过使用std::ref来执行。
因此,最终版本可能是:
#include <functional>
int foo(void) {return 2;}
class bar {
public:
int operator() (void) {return 3;};
int something(int a) {return a;};
};
template <class C> auto func(C&& c) -> decltype(c()) { return c(); }
template <class C> int doit(C&/*&*/ c) // r-value reference can't be forwarded via std::bind
{
return c();
}
template <class C> void func_wrapper(C&& c)
{
func(std::bind(doit<C>,
/* std::forward<C>(c) */ // forget pefect forwarding while using std::bind
std::ref(c)) // try to pass the functor itsself instead of its return value
);
}
int main(int argc, char* argv[])
{
// call with a function pointer
func(foo);
func_wrapper(foo); // error disappears
// call with a member function
bar b;
func(b);
func_wrapper(b);
// call with a bind expression
func(std::bind(&bar::something, b, 42));
func_wrapper(std::bind(&bar::something, b, 42)); // error disappears
// call with a lambda expression
func( [](void)->int {return 42;} );
func_wrapper( [](void)->int {return 42;} );
return 0;
}
但是,如果你真的想达到目的1和2,该怎么办呢?尝试这个:
#include <functional>
#include <iostream>
void foo()
{
}
struct bar {
void operator()() {}
void dosomething() {}
};
static bar b;
template <typename Executor>
void run(Executor&& e)
{
std::cout << "r-value reference forwarded\n";
e();
}
template <typename Executor>
void run(Executor& e)
{
std::cout << "l-value reference forwarded\n";
e();
}
template <typename Executor>
auto func(Executor&& e) -> decltype(e())
{
return e();
}
template <bool b>
struct dispatcher_traits {
enum { value = b };
};
template <typename Executor, bool is_lvalue_reference>
class dispatcher {
private:
static void dispatch(Executor& e, dispatcher_traits<true>)
{
run(e);
}
static void dispatch(Executor& e, dispatcher_traits<false>)
{
run(std::ref(e));
}
public:
static void forward(Executor& e)
{
dispatch(e, dispatcher_traits<is_lvalue_reference>());
}
};
template <typename Executor>
void func_wrapper(Executor&& e)
{
typedef dispatcher<Executor,
std::is_lvalue_reference<Executor>::value>
dispatcher_type;
func(std::bind(&dispatcher_type::forward, std::ref(e)));
}
int main()
{
func_wrapper(foo); // l-value
func_wrapper(b); // l-value
func_wrapper(bar()); // r-value
func_wrapper(std::bind(&bar::dosomething, &b)); // r-value
func_wrapper([](){}); // r-value
}
让我解释一下几点:
- 为了减少大量 return 语句,将函子签名从 int() 更改为 void()。
- 2个run()函数模板用于检查原始函子参数是否完美转发。
- Dispatcher_traits 会将 bool 常量映射到类型。
- 您最好将 Dispatcher::forward 命名为与 Dispatcher::dispatch 不同,否则您必须使用 Dispatcher::forward 的签名调用 std::bind 模板。