2014-12-30 181 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()'。 –