21

I'm having trouble in detecting why the heck is this not compiling. I've got some lambda function that returns a std::function based on some argument.

I've narrowed down my problem to this snippet(which doesn't use lambdas, but reproduces my error perfectly):

#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();
}

The call to w() produces one of those lovely gcc error outputs in which I can't figure out what's going wrong. This is the error echoed by 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>)>}]

Here, f should be some callable object which takes an int as argument and calls x.bar(int) using it. On the other hand, w is just a callable object which calls some_fun(f), being f the callable object mentioned above, which has the signature expected by some_fun's parameter.

Am I missing something? I probably don't know how to actually mix std::bind and std::function.

mfontanini
  • 21,410
  • 4
  • 65
  • 73
  • 2
    It seems to work when you replace auto with `std::function` for f. –  May 27 '12 at 21:36
  • 2
    This might not be an answer to your problem.. but have you considered using the native lambda-functions coming with C++11 (which make std::bind unnecessary)? – coldfix May 27 '12 at 21:39
  • 4
    In Boost we have `protect` for cases like this, but it doesn't seem to have make it to the standard. – K-ballo May 27 '12 at 21:39
  • @EthanSteinberg nice one, didn't try that. It does work that way. – mfontanini May 27 '12 at 21:41
  • So the question is, what *is* auto taking the type of? Perhaps it's an `std::mem_fn` or something. I haven't worked with this a lot. – chris May 27 '12 at 21:42
  • @chris yes, apparently bind is changing the expected(at least by me) type of `f`. I mean, I know that `std::bind` return some form of magic type, but hadn't thought it couldn't be used as an `std::function`. – mfontanini May 27 '12 at 21:47
  • 7
    `f` isn't a `std::function`, but rather some unnamable bind-expression type. You have to convert it to `std::function` explicitly. This is one of the cases where you mustn't use `auto`. – Kerrek SB May 27 '12 at 21:47
  • @KerrekSB, if you can put in a bit more *why*, that would make a great answer. – chris May 27 '12 at 21:49
  • @KerrekSB I agree with chris ;) – mfontanini May 27 '12 at 21:52
  • 1
    See [this answer](http://stackoverflow.com/a/10555796/636019), specifically the part about preventing eager evaluation (you can't without changing the type, because of `std::is_bind_expression<>`). – ildjarn May 27 '12 at 21:53

2 Answers2

21

std::bind expressions, like their boost::bind predecessors, support a type of composition operation. Your expression for w is roughly equivalent to

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

Nesting binds in this manner is interpreted as

  1. Calculate the value of x.bar<int>(y) where y is the first parameter passed into the resulting functor.
  2. Pass that result into some_fun.

But x.bar<int>(y) returns void, not any function type. That's why this doesn't compile.

As K-ballo points out, with boost::bind, you can fix this problem with boost::protect. As Kerrek SB and ildjarn point out, one way around this issue is: don't use auto for f. You don't want f to have the type of a bind expression. If f has some other type, then std::bind won't attempt to apply the function composition rules. You might, for instance, give f the type 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);

Since f doesn't literally have the type of a bind expression, std::is_bind_expression<>::value will be false on f's type, and so the std::bind expression in the second line will just pass the value on verbatim, rather than attempting to apply the function composition rules.

Managu
  • 8,849
  • 2
  • 30
  • 36
-3

some_fun wants argument of type const std::function<void(int)> &.

std::bind returns "a function object of unspecified type T" (look at provided link, section "Return value"), that you are trying to pass as some_fun argument.

It seems this causes problem, because this argument type is not expected.

Look at: http://en.cppreference.com/w/cpp/utility/functional/bind

Ruben
  • 2,488
  • 1
  • 18
  • 22
  • 2
    This is not the underlying problem at all -- `std::is_bind_expression<>` and its behavior is. – ildjarn May 27 '12 at 22:36