std :: bind和完美转发

Igo*_* R. 5 c++ stdbind perfect-forwarding c++11

以下代码无法编译:

#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);
}
Run Code Online (Sandbox Code Playgroud)

如果我理解正确的,原因如下:std::bind会将它的参数,当binderoperator()叫,它通过了所有绑定的参数作为左值 -即使是那些进入者bind右值.但是invoke为原始参数进行了实例化,它无法接受binder传递它的尝试.

这个问题有什么解决方案吗?

Bar*_*rry 5

您的理解是正确的 - bind复制其论点.所以你必须提供invoke()在左值上调用的正确重载:

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

这适用于大多数类型.[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;
Run Code Online (Sandbox Code Playgroud)

将其重新插入原始解决方案中,我们也可以获得适合的解决方案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();
}
Run Code Online (Sandbox Code Playgroud)

当然,如果其中一个Arg是占位符,那么无论如何都不会起作用.如果它是一个绑定表达式,你还必须写一些其他东西.