2015-06-21 58 views
5

下面的代碼無法編譯:的std ::綁定和完美轉發

#include <functional> 

template<class ...Args> 
void invoke(Args&&... args) 
{ 
} 

template<class ...Args> 
void bind_and_forward(Args&&... args) 
{ 
    auto binder = std::bind(&invoke<Args...>, std::forward<Args>(args)...); 
    binder(); 
} 

int main() 
{ 
    int a = 1; 
    bind_and_forward(a, 2); 
} 

如果我理解正確的,原因如下:std::bind會將它的參數,而當binderoperator()叫,它將所有綁定參數作爲左值 - 即使那些輸入bind的值爲右值。但是invoke被初始化爲原始參數,並且它不能接受binder試圖通過它的內容。

有沒有解決這個問題的方法?

回答

4

你的理解是正確的 - bind複製它的論點。所以,你必須提供的invoke()正確的過載,將在左值被稱爲:

template<class ...Args> 
void bind_and_forward(Args&&... args) 
{ 
    auto binder = std::bind(&invoke<Args&...>, std::forward<Args>(args)...); 
            ^^^^^^^^ 
    binder(); 
} 

這適用於大多數類型。 operator()的[func.bind.bind]中列舉了一些例外,其中Arg&不足。正如你指出的那樣,其中一個是std::reference_wrapper<T>。我們可以通過用類型特徵替換上面的Args&用法來解決這個問題。通常情況下,我們就只需要添加一個左參考,但對於reference_wrapper<T>,我們只想T&

template <typename Arg> 
struct invoke_type 
: std::add_lvalue_reference<Arg> { }; 

template <typename T> 
struct invoke_type<std::reference_wrapper<T>> { 
    using type = T&; 
}; 

template <typename T> 
using invoke_type_t = typename invoke_type<T>::type; 

插件是回到原來的解決方案,我們得到的東西,對於reference_wrapper過的作品:

template<class ...Args> 
void bind_and_forward(Args&&... args) 
{ 
    auto binder = std::bind(&invoke<invoke_type_t<Args>...>, 
          //  ^^^^^^^^^^^^^^^^^^^ 
          std::forward<Args>(args)...); 
    binder(); 
} 

當然,如果其中一個Arg是佔位符,則無論如何這都不起作用。如果它是一個綁定表達式,那麼你也必須寫一些其他的東西。

+0

但是在這種情況下,它不會編譯用'std :: ref()'包裝的參數。 –

+0

@IgorR。更新了那個 - 'bind()'爲'std :: ref()'做了一些特殊的處理。所以你必須做一些特別的事情。 – Barry