std::bind and perfect forwarding

大兔子大兔子 提交于 2019-11-30 17:11:11

问题


The following code does not compile:

#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);
}

If I understand correctly, the reason is as follows: std::bind copies its arguments, and when the binder's operator() is called, it passes all the bound arguments as lvalues - even those ones that entered bind as rvalues. But invoke was instantiated for the original arguments, and it can't accept what the binder attempts to pass it.

Is there any solution for this problem?


回答1:


Your understanding is correct - bind copies its arguments. So you have to provide the correct overload of invoke() that would be called on the lvalues:

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

This works on most types. There are a few exceptions enumerated in [func.bind.bind] for operator(), where Arg& is insufficient. One such, as you point out, is std::reference_wrapper<T>. We can get around that by replacing the Args&usage above with a type trait. Typically, we'd just add an lvalue reference, but for reference_wrapper<T>, we just want 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;

Plug that back into the original solution, and we get something that works for reference_wrapper too:

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

Of course, if one of Arg is a placeholder this won't work anyway. And if it's a bind expression, you'll have to write something else too.



来源:https://stackoverflow.com/questions/30968573/stdbind-and-perfect-forwarding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!