std::bind and perfect forwarding

后端 未结 1 1629
温柔的废话
温柔的废话 2021-01-06 02:54

The following code does not compile:

#include 

template
void invoke(Args&&... args)
{
}

template

        
相关标签:
1条回答
  • 2021-01-06 03:15

    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.

    0 讨论(0)
提交回复
热议问题