Автоматический вывод типа с функтором не работает

Возможный дубликат:
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);
}
Другие вопросы по тегам