Подтвердить что ты не робот

Std:: связать связанную функцию

У меня возникли проблемы с обнаружением того, почему это не компиляция. У меня есть функция лямбда, которая возвращает std::function на основе некоторого аргумента.

Я сузил свою проблему до этого фрагмента (который не использует lambdas, но отлично воспроизводит мою ошибку):

#include <functional>
#include <iostream>


struct foo {
    template<class T>
    void bar(T data) {
        std::cout << data << "\n";
    }
};

void some_fun(const std::function<void(int)> &f) {
    f(12);
}

int main() {
    foo x;
    auto f = std::bind(&foo::bar<int>, x, std::placeholders::_1);
    auto w = std::bind(some_fun, f);
    w();
}

Вызов w() вызывает один из этих прекрасных gcc-ошибок, в которых я не могу понять, что происходит. Это ошибка, отраженная gcc 4.6.1:

g++ -std=c++0x    test.cpp   -o test
test.cpp: In function ‘int main()’:
test.cpp:20:7: error: no match for call to ‘(std::_Bind<void (*(std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>))(const std::function<void(int)>&)>) ()’
/usr/include/c++/4.6/functional:1130:11: note: candidates are:
/usr/include/c++/4.6/functional:1201:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}]
/usr/include/c++/4.6/functional:1215:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}]
/usr/include/c++/4.6/functional:1229:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}]
/usr/include/c++/4.6/functional:1243:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}]

Здесь f должен быть некоторый вызываемый объект, который принимает int как аргумент и вызывает x.bar(int), используя его. С другой стороны, w является только вызываемым объектом, который вызывает some_fun(f), будучи f вызываемым объектом, упомянутым выше, который имеет подпись, ожидаемую параметром some_fun.

Я что-то упустил? Я, вероятно, не знаю, как на самом деле смешивать std::bind и std::function.

4b9b3361

Ответ 1

std::bind выражения, как и их предшественники boost::bind, поддерживают тип операции композиции. Ваше выражение для w примерно эквивалентно

auto w=std::bind(some_fun,  std::bind(&foo::bar<int>, x, std::placeholders::_1) );

Связывание вложенности таким образом интерпретируется как

  • Вычислить значение x.bar<int>(y), где y - это первый параметр, переданный в результирующий функтор.
  • Передайте этот результат в some_fun.

Но x.bar<int>(y) возвращает void, а не какой-либо тип функции. Вот почему это не компилируется.

Как указывает K-ballo, с boost::bind вы можете исправить эту проблему с помощью boost::protect. Как указывают Kerrek SB и ildjarn, один из способов решения этой проблемы: не используйте auto для f. Вы не хотите, чтобы f имел тип выражения привязки. Если f имеет другой тип, то std::bind не будет пытаться применять правила композиции функции. Например, вы можете дать f тип std::function<void(int)>:

std::function<void(int)> f = std::bind(&foo::bar<int>, x, std::placeholders::_1);
auto w = std::bind(some_fun, f);

Так как f буквально не имеет тип выражения привязки, std::is_bind_expression<>::value будет ложным в типе f, и поэтому выражение std::bind во второй строке просто передаст значение на дословно, а не пытаться применить правила композиции функций.

Ответ 2

some_fun требуется аргумент типа const std::function<void(int)> &.

std:: bind возвращает "объект функции неопределенного типа T" (смотрите предоставленную ссылку, раздел "Возвращаемое значение" ), которую вы пытаетесь передать как аргумент some_fun.

Кажется, это вызывает проблему, потому что этот тип аргумента не ожидается.

Посмотрите: http://en.cppreference.com/w/cpp/utility/functional/bind