这是my previous question的后续问题。
#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) { return c();}
template <class C> void func_wrapper(C&& c) { func( std::bind(doit<C>, std::forward<C>(c)) ); }
int main(int argc, char* argv[])
{
// call with a function pointer
func(foo);
func_wrapper(foo); // error
// 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
// call with a lambda expression
func( [](void)->int {return 42;} );
func_wrapper( [](void)->int {return 42;} );
return 0;
}
我在C ++标题中深陷了编译错误:
functional:1137: error: invalid initialization of reference of type ‘int (&)()’ from expression of type ‘int (*)()’
functional:1137: error: conversion from ‘int’ to non-scalar type ‘std::_Bind<std::_Mem_fn<int (bar::*)(int)>(bar, int)>’ requested
func_wrapper(foo)应该执行func(doit(foo))。在实际代码中,它打包了要执行的线程的函数。 func将由另一个线程执行该函数,它们介于两者之间以检查未处理的异常并进行清理。但func_wrapper中的额外绑定会让事情变得混乱......
答案 0 :(得分:2)
现在第二次看这个,我想我对你看到的第一个错误有一个可信的解释。
在这种情况下,查看完整错误和导致它的模板实例更有帮助。例如,我的编译器(GCC 4.4)打印的错误以以下行结束:
test.cpp:12: instantiated from ‘decltype (c()) func(C&&) [with C = std::_Bind<int (*(int (*)()))(int (&)())>]’
test.cpp:16: instantiated from ‘void func_wrapper(C&&) [with C = int (&)()]’
test.cpp:22: instantiated from here
/usr/include/c++/4.4/tr1_impl/functional:1137: error: invalid initialization of reference of type ‘int (&)()’ from expression of type ‘int (*)()’
现在看看这个自下而上,实际的错误信息似乎是正确的;编译器推断的类型 不兼容。
第一个模板实例化,func_wrapper
,清楚地显示了编译器从foo
中的实际参数func_wrapper(foo)
中推断出的类型。我个人认为这是一个函数指针,但它实际上是一个函数引用。
第二个模板实例化几乎不可读。但是稍微弄乱了std::bind
,我了解到GCC的文本表示格式打印为绑定仿函数大致是:
std::_Bind<RETURN-TYPE (*(BOUND-VALUE-TYPES))(TARGET-PARAMETER-TYPES)>
将它拆开:
std::_Bind<int (*(int (*)()))(int (&)())>
// Return type: int
// Bound value types: int (*)()
// Target parameter types: int (&)()
这是不兼容类型的起始位置。显然,即使c
中的func_wrapper
是函数引用,它也会在传递给std::bind
后变成函数指针,从而导致类型不兼容。对于它的价值,std::forward
在这种情况下根本不重要。
我的理由是std::bind
似乎只关心价值观,而不是引用。在C / C ++中,没有函数值这样的东西;只有引用和指针。因此,当取消引用函数引用时,编译器只能有意义地为您提供函数指针。
对此唯一的控制是模板参数。您必须告诉编译器您正在处理一个函数指针从头开始才能使其工作。无论如何,这可能是你的想法。为此,请为模板参数C
明确指定所需的类型:
func_wrapper<int (*)()>(foo);
或者更简洁的解决方案,明确地采用函数的地址:
func_wrapper(&foo); // with C = int (*)()
如果我弄清楚第二个错误,我会回复你的。 :)
答案 1 :(得分: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无法将r值引用正确转发到该函数。 reason已经详细说明了。
所以,让我们看一下这个问题。这里最重要的功能可能是func_wrapper,它旨在实现3个目的:
根据b点,目的1无法实现。所以,让我们忘记完美转发和doit函数模板必须接受l值引用参数。
根据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
}
让我解释一下: