std :: bind和完美转发

时间:2015-06-21 19:39:08

标签: c++ c++11 perfect-forwarding stdbind

以下代码无法编译:

#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复制其参数,当调用binder&#39; operator()时,它会传递所有绑定的参数as lvalues - 即使那些输入bind rvalues 的人也是如此。但invoke已针对原始参数进行了实例化,并且它无法接受binder尝试传递的内容。

这个问题有解决办法吗?

1 个答案:

答案 0 :(得分:5)

您的理解是正确的 - bind复制其论点。因此,您必须提供将在左值上调用的invoke()的正确重载:

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

这适用于大多数类型。 [func.bind.bind]中为operator()列举了一些例外情况,其中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中的一个是占位符,那么无论如何都无法工作。如果它是一个绑定表达式,你也必须写一些别的东西。