Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::bind a bound function

Tags:

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.

like image 435
mfontanini Avatar asked May 27 '12 21:05

mfontanini


People also ask

What does bind function do in C++?

std::bind. Returns a function object based on fn , but with its arguments bound to args . Each argument may either be bound to a value or be a placeholder: - If bound to a value, calling the returned function object will always use that value as argument.

How does STD bind work?

std::bind allows you to create a std::function object that acts as a wrapper for the target function (or Callable object). std::bind also allows you to keep specific arguments at fixed values while leaving other arguments variable.

Is std :: bind deprecated?

Yes: std::bind should be replaced by lambda For almost all cases, std::bind should be replaced by a lambda expression. It's idiomatic, and results in better code. There is almost no reason post C++11 to use std::bind .

Does STD bind allocate?

As far as I can tell std::bind just concatenates stuff together into an object, so binding anything to a member function will result in an object that is at least three pointers in size. Assigning this object to a std::function will result in dynamic memory allocation.


1 Answers

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.

like image 176
Managu Avatar answered Sep 29 '22 11:09

Managu