2015-06-21 2 views
5

Следующий код не компилируется:станд :: свяжи и совершенный экспедиторская

#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 «s operator(), он передает все связанные аргументы как lvalues ​​ - даже те, которые ввели bind как rvalues ​​. Но invoke был создан для оригинальных аргументов, и он не может принять то, что binder пытается его передать.

Есть ли решение проблемы?

ответ

4

Ваше понимание неверное - bind копирует свои аргументы. Таким образом, вы должны обеспечить правильную перегрузку invoke(), которая будет называться по lvalues:

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& типом типа. Как правило, мы бы просто добавить Lvalue ссылку, но для 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 является заполнителем, это не будет работать. И если это выражение привязки, вам придется написать что-то еще.

+0

Но в таком случае он не будет компилироваться для аргумента, завернутого в 'std :: ref()'. –

+0

@IgorR. Обновлено для этого - 'bind()' делает что-то особенное для 'std :: ref()'. Так что вам придется делать что-то особенное. – Barry