std::bind和完美转发

7
以下代码无法编译:
#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 尝试传递给它的内容。

这个问题有解决方法吗?

1个回答

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之一是占位符,这种方法也不起作用。而且如果它是一个绑定表达式,你还需要另外写些东西。


但是在这种情况下,如果参数被std::ref()包装,它将无法编译。 - Igor R.
@IgorR。更新了这个 - bind() 对于 std::ref() 做了一些特殊处理。所以你也需要做一些特殊处理。 - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接