2010-04-29 138 views
13

这是对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(富)应该执行FUNC(DOIT(富) )。在真正的代码中,它封装了要执行的线程的函数。函数会由另一个线程执行的函数,doit坐在中间检查未处理的异常并清理。但在func_wrapper附加绑定食堂的事情了......

+0

也许删除C++标记?这是直的C++ 0x – Anycorn 2010-04-29 20:33:33

+10

保留这两个标签。 C++ 0x也是C++。 – jalf 2010-04-29 22:44:22

+0

给PC-Lint一个旋风(来自www.gimpel.com)。它比Visual Studio提供更好更详细的错误消息非常好。但是,它非常昂贵。 – 2010-05-14 10:55:52

回答

2

现在这个第二次看,我觉得我对你所看到的第一个错误plausable解释。

在这种情况下,它更有助于看完整的错误,并导致了它的模板实例。通过我的编译器(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清楚地显示了编译器从func_wrapper(foo)中的实际参数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 (&)() 

这是不兼容类型开始。显然,即使在cfunc_wrapper是一个函数的参考,它变成一次传递给std::bind函数指针,造成类型不兼容。在这种情况下,std::forward完全不重要。

我在这里的理由是,std::bind似乎只关心价值,而不是引用。在C/C++中,函数的值不存在;只有引用和指针。所以当函数引用被取消引用时,编译器只能有意义地给你一个函数指针。

你有过这种情况的唯一控制是你的模板参数。您将不得不告诉编译器,您正在处理从开始开始的函数指针以完成此工作。无论如何,这可能就是你想到的。要做到这一点,明确指定要用于模板参数C类型:

func_wrapper<int (*)()>(foo); 

或者更简单的解决方案,明确地把函数的地址:

func_wrapper(&foo); // with C = int (*)() 

我会仿佛回到了你我曾经弄清楚第二个错误。 :)

1

在开始的时候,请让我介绍2关键点:

  • 一个:当使用嵌套的std ::绑定,内部的std ::绑定首先计算,并且返回在评估外部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个用途:

  1. 完美转发函子起初大一函数模板,
  2. 然后使用std ::绑定,使DOIT为关闭,
  3. 并让func函数模板最后执行std :: bind返回的函子。

根据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,怎么样?试试这个:

让我解释一下几点:

  • 为了减少大量return语句的,从int改变仿签名()为void()。
  • 2 run()函数模板用于检查原始仿函数参数是否完美转发。
  • dispatcher_traits将要将bool常量映射到类型。
  • 您最好将dispatcher :: forward命名为dispatcher :: dispatch,或者您必须使用dispatcher :: forward的签名来调用std :: bind模板。