Автоматический вывод типа с функтором не работает
Возможный дубликат:
std:: bind связанной функции
void foo0(int val) { std::cout << "val " << val << "\n"; }
void foo1(int val, std::function<void (int)> ftor) { ftor(val); }
void foo2(int val, std::function<void (int)> ftor) { ftor(val); }
int main(int argc, char* argv[]) {
auto applyWithFoo0 ( std::bind(foo0, std::placeholders::_1) );
//std::function<void (int)> applyWithFoo0 ( std::bind(foo0, std::placeholders::_1) ); // use this instead to make compile
auto applyFoo1 ( std::bind(foo1, std::placeholders::_1, applyWithFoo0) );
foo2(123, applyFoo1);
}
Приведенный выше пример не компилируется, давая несколько ошибок, таких как: Error 1 error C2780: '_Ret std::tr1::_Callable_fun<_Ty,_Indirect>::_ApplyX(_Arg0 &&,_Arg1 &&,_Arg2 &&,_Arg3 &&,_Arg4 &&,_Arg5 &&,_Arg6 &&,_Arg7 &&,_Arg8 &&,_Arg9 &&) const' : expects 10 arguments - 2 provided
,
Использование закомментированной строки с явным типом компилирует. Кажется, что тип, выведенный auto
не является правильным. В чем проблема auto
в этом случае?
Платформа: MSVC 10 SP 1, GCC 4.6.1
2 ответа
Проблема в том, что std::bind
относится к "связать выражение" (как ваш applyWithFoo0
) в отличие от других типов. Вместо вызова foo1 с applyWithFoo0
в качестве параметра он пытается вызвать applyWithFoo0
и передайте его возвращаемое значение в foo1. Но applyWithFoo0
не возвращает ничего конвертируемого в std::function<void(int)>
, Цель обработки "выражений связывания", как это, состоит в том, чтобы сделать их легко компонуемыми. В большинстве случаев вы, вероятно, не хотите, чтобы выражение связывания передавалось как параметры функции, а только как их результаты. Если вы явно оберните выражение связывания в function<>
объект, function<>
объект будет просто передан в foo1 напрямую, так как он не является "выражением привязки" и поэтому не обрабатывается специально std::bind
,
Рассмотрим следующий пример:
#include <iostream>
#include <functional>
int twice(int x) { return x*2; }
int main()
{
using namespace std;
using namespace std::placeholders;
auto mul_by_2 = bind(twice,_1);
auto mul_by_4 = bind(twice,mul_by_2); // #2
auto mul_by_8 = bind(twice,mul_by_4); // #3
cout << mul_by_8(1) << endl;
}
Это на самом деле компилируется и работает, потому что вместо передачи функтора в два раза больше, чем вы могли бы ожидать от выражений связывания #2 и #3, связывание фактически оценивает переданные выражения связывания и дважды использует свой результат в качестве параметра функции. Здесь это намеренно. Но в вашем случае вы случайно отключили это поведение, потому что вы на самом деле хотите, чтобы bind передавал сам функтор функции вместо ее оцененного значения. Обертывание функтора в объект function<>, очевидно, является обходным решением.
На мой взгляд, это дизайнерское решение немного неловко, потому что оно вводит нерегулярность, которую люди должны знать о том, как правильно использовать связывание. Может быть, мы получим еще более приятную работу в будущем, как
auto applyFoo1 = bind( foo1, _1, noeval(applyWithFoo0) );
где noeval
говорит bind не оценивать выражение, а передавать его напрямую функции. Но, возможно, наоборот - явное указание bind передать результат функтора в функцию - было бы лучше:
auto mul_by_8 = bind( twice, eval(mul_by_4) );
Но, думаю, сейчас уже слишком поздно...
Я предполагаю, что круглые скобки вокруг std::bind заставляют парсер думать, что вы объявляете функции с именами applyWithFoo0 и applyFoo1.
std::bind возвращает функтор, тип которого auto должен уметь обнаруживать.
Попробуй это:
int main(int argc, char* argv[]) {
auto applyWithFoo0 = std::bind(foo0, std::placeholders::_1);
//std::function<void (int)> applyWithFoo0 std::bind(foo0, std::placeholders::_1) ); // use this instead to make compile
auto applyFoo1 = std::bind(foo1, std::placeholders::_1, applyWithFoo0);
foo2(123, applyFoo1);
}