2014-12-30 83 views
0

我有一个类foo与方法bar其中(函数指针/函数)可调用的东西。这个可调用的东西应该被传递到另一个方法doit作为一个绑定元素与第三种方法bar_cb方法。绑定绑定函数作为参数

#include <functional> 
#include <iostream> 

class foo { 
public: 
    template<typename T> 
    void bar(T&& t) { 
     std::cout << "bar\n"; 
     doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t))); 
    } 

    template<typename T> 
    void doit(T&& t) { 
     std::cout << "doit\n"; 
     t(); 
    } 

    template<typename T> 
    void bar_cb(T&& t) { 
     std::cout << "bar_cb\n"; 
     t(); 
    } 
}; 


void lala() { 
    std::cout << "lala\n"; 
} 

class functor { 
public: 
    void operator()() { 
     std::cout << "functor::operator()\n"; 
    } 
}; 


int main() { 
    foo f; 
    functor fn; 
    f.bar(fn); 
    f.bar(std::bind(lala)); // error 

    return 0; 
} 

这工作正常functors而不是绑定功能作为论据foo::barlala在我的例子)。是否有可能将一个不可知的类型传递给一个方法,并将其作为另一个参数的绑定方法(以及如何)?我知道我可以在函数中包含一个函子(例如std::function),但由于我可以调用一个不可知类型,我认为还有一种方法可以绑定它(我想我只是缺少一些简单的东西)。

Here指向示例的链接。

+0

什么是实际(更高级别)你正试图解决的问题? –

+0

@JohnZwinck问题是,我想有一个类,它可以是一个函子或函数,并用这个'callable'作为参数调用其他函子。真正的代码是一个可变模板类,具有不同的函数作为模板参数。然而,我试图将问题归结为一个简单的非模板类来理解错误。我也想避免'std :: function'的开销。 – user1810087

+0

您正在运行bind绑定表达式的特殊处理。 http://stackoverflow.com/questions/10777421/stdbind-a-bound-function –

回答

2

主要问题是您的bar_cb(T&&)不会推导出模板参数,因为在使用&foo::template bar_cb<X>以及某些模板参数X时实际指定了模板参数。然而,bind()表达式将复制绑定函数,即它可能具有或可能不具有将被推导的类型。另外,std::bind()不是bind()-表达通过,而宁可电话他们!

最简单的解决办法是使用std::bind()绑定功能,而是使用lambda功能:

template<typename T> 
void bar(T&& t) { 
    std::cout << "bar\n"; 
    doit([=](){ this->bar_cb(t); }); 
} 

这样做让编译器推断为bar_cb()校正参数型(C + +14你可能想使用捕获[this,t = std::forward<T>(t)],虽然你的bar_cb()仍然不会看到右值)。

要传递一个已经bind() -expression通过另一个bind() -expression,而不必考虑bind()bind() -expression一个bind() -expression你需要使它看起来好像它是不是一个bind() -expression。你可以用薄函数包装这么做:

template <typename Fun> 
class unbinder { 
    Fun fun; 
public: 
    template <typename F> 
    unbinder(F&& fun): fun(std::forward<F>(fun)) {} 
    template <typename... Args> 
    auto operator()(Args&&... args) const 
     -> decltype(fun(std::forward<Args>(args)...)) { 
     return fun(std::forward<Args>(args)...); 
    } 
}; 
template <typename Fun> 
auto unbind(Fun&& fun) 
    -> unbinder<Fun> { 
    return unbinder<Fun>(std::forward<Fun>(fun)); 
} 

由于存储在bind()表达的功能将左值传递,你需要一个不同的声明为您bar_cb(),但是:

template<typename T> 
void bar_cb(T& t) { 
    ... 
} 

有了这一点,你可以用

f.bar(unbind(std::bind(lala))); 

如果你想使用注册bind() -expression您需要的bar()条件的定义:如果它收到一个bind() -expression它需要自动隐藏的事实,这是一个bind() -expression通过应用unbind()或类似的东西:

template<typename T> 
typename std::enable_if<!std::is_bind_expression<typename std::decay<T>::type>::value>::type 
bar(T&& t) { 
    std::cout << "bar (non-bind)\n"; 
    doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t))); 
} 
template<typename T> 
typename std::enable_if<std::is_bind_expression<typename std::decay<T>::type>::value>::type 
bar(T&& t) { 
    std::cout << "bar (bind)\n"; 
    doit(std::bind(&foo::template bar_cb<unbinder<T>>, this, unbind(std::forward<T>(t)))); 
} 
+0

谢谢你这个详细的答案。我会尝试这两个。btw,是否有一个特定的原因为什么你在lambda函数中通过值传递 – user1810087

+0

通过值捕获绑定参数类似于原始构造使用'srd :: bind()'更紧密(如果你想通过引用wirh'std :: bind()来捕获这个值,你可以使用'std :: ref()')。另外,它似乎是因为没有终身问题,所以更具有防故障的起点通过值使用泛型参数给函数的用户选择覆盖缺省值,也使用'std :: ref()'。 –