2015-06-21 57 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