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
.
Ответы
Ответ 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