Std::Bind a Bound Function

std::bind a bound function

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.

A problem that using std::bind to bind a function

Lvalue cannot be bound to rvalue reference.

When you have:

void bar(int&&) {}
int i = 0;
bar(i); // error

the last line will not compile.

This is the reason your code fails in second case.


std::bind takes all passed arguments and copies/moves them into data member of newly generated functor:

std::bind(func,arg1,arg2)

gives you:

class closure1 {
Arg1 arg1;
Arg2 arg2;

void operator()() {
func(arg1,arg2);
}
};

and it provides also function call operator in which, arg(s) are passed by value as Lvalues (4th dot in Member function operator() section of std::bind reference:

Otherwise, the ordinary stored argument arg is passed to the invokable
object as lvalue argument: the argument vn in the std::invoke call
above is simply arg and the corresponding type Vn is T cv &, where cv
is the same cv-qualification as that of g.

So this:

auto f = std::bind(func, 1, "100", std::forward<F>(f2));

generates

class closure2 {
int i = 1;
std::string s = "100";
std::forward<void(int)> f;
void operator()() {
func(i,s,f); // [1]
}
};

and in [1] is problem, because f as lvalue cannot be bound to rvalue reference declared in:

void func(int n, std::string s, std::function<void(int)> &&subf)

you can add another overload taking lvalue reference:

void func2(int n, std::string s, std::function<void(int)> &subf)

and call bind with that version of your overloads.


There is no problem with bind_front_handler because in this implementation all data members of generated functor are forwarded to a target:

 func(...,std::forward< std::function<void()> >(f));

so f will be casted into rvalue reference, and func can accept this argument.


Demo

std::bind - vector as an argument in bound function

It really depends on what you want to do with the bound functions.

If they are going to be copied passed around (beyond the life of vv), this is correct (and is going to copy vv).

auto f1 = std::bind(&foo, vv); // 1)

This is also correct, (vv is not going to be copied initially at least.)

auto f2 = std::bind(&foo, std::move(vv)); // 2)

but you will not have access to vv after that point.

This is however the most likely scenario that I can deduce from your example:
if the bound function will be used locally while vv is still alive it is more likely from the example that want you want f3 to hold a "reference" to vv. This is done with the ref convention:

auto f3 = std::bind(&foo, std::ref(vv));

how to pass a std::bind object to a function

You are using the wrong placeholders, you need _1:

auto fun = std::bind(&keyFormatter, sKeyFormat, std::placeholders::_1);

The number of the placeholder is not here to match the position of the args, but rather to choose which arguments to send to the original function in which position:

void f (int, int);

auto f1 = std::bind(&f, 1, std::placeholders::_1);
f1(2); // call f(1, 2);
auto f2 = std::bind(&f, std::placeholders::_2, std::placeholders::_1);
f2(3, 4); // call f(4, 3);
auto f3 = std::bind(&f, std::placeholders::_2, 4);
f3(2, 5); // call f(5, 4);

See std::bind, especially the examples at the end.

How std::bind works with member functions

When you say "the first argument is a reference" you surely meant to say "the first argument is a pointer": the & operator takes the address of an object, yielding a pointer.

Before answering this question, let's briefly step back and look at your first use of std::bind() when you use

std::bind(my_divide, 2, 2)

you provide a function. When a function is passed anywhere it decays into a pointer. The above expression is equivalent to this one, explicitly taking the address

std::bind(&my_divide, 2, 2)

The first argument to std::bind() is an object identifying how to call a function. In the above case it is a pointer to function with type double(*)(double, double). Any other callable object with a suitable function call operator would do, too.

Since member functions are quite common, std::bind() provides support for dealing with pointer to member functions. When you use &print_sum you just get a pointer to a member function, i.e., an entity of type void (Foo::*)(int, int). While function names implicitly decay to pointers to functions, i.e., the & can be omitted, the same is not true for member functions (or data members, for that matter): to get a pointer to a member function it is necessary to use the &.

Note that a pointer to member is specific to a class but it can be used with any object that class. That is, it is independent of any particular object. C++ doesn't have a direct way to get a member function directly bound to an object (I think in C# you can obtain functions directly bound to an object by using an object with an applied member name; however, it is 10+ years since I last programmed a bit of C#).

Internally, std::bind() detects that a pointer to a member function is passed and most likely turns it into a callable objects, e.g., by use std::mem_fn() with its first argument. Since a non-static member function needs an object, the first argument to the resolution callable object is either a reference or a [smart] pointer to an object of the appropriate class.

To use a pointer to member function an object is needed. When using a pointer to member with std::bind() the second argument to std::bind() correspondingly needs to specify when the object is coming from. In your example

std::bind(&Foo::print_sum, &foo, 95, _1)

the resulting callable object uses &foo, i.e., a pointer to foo (of type Foo*) as the object. std::bind() is smart enough to use anything which looks like a pointer, anything convertible to a reference of the appropriate type (like std::reference_wrapper<Foo>), or a [copy] of an object as the object when the first argument is a pointer to member.

I suspect, you have never seen a pointer to member - otherwise it would be quite clear. Here is a simple example:

#include <iostream>

struct Foo {
int value;
void f() { std::cout << "f(" << this->value << ")\n"; }
void g() { std::cout << "g(" << this->value << ")\n"; }
};

void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
(foo1->*fun)(); // call fun on the object foo1
(foo2->*fun)(); // call fun on the object foo2
}

int main() {
Foo foo1{1};
Foo foo2{2};

apply(&foo1, &foo2, &Foo::f);
apply(&foo1, &foo2, &Foo::g);
}

The function apply() simply gets two pointers to Foo objects and a pointer to a member function. It calls the member function pointed to with each of the objects. This funny ->* operator is applying a pointer to a member to a pointer to an object. There is also a .* operator which applies a pointer to a member to an object (or, as they behave just like objects, a reference to an object). Since a pointer to a member function needs an object, it is necessary to use this operator which asks for an object. Internally, std::bind() arranges the same to happen.

When apply() is called with the two pointers and &Foo::f it behaves exactly the same as if the member f() would be called on the respective objects. Likewise when calling apply() with the two pointers and &Foo::g it behaves exactly the same as if the member g() would be called on the respective objects (the semantic behavior is the same but the compiler is likely to have a much harder time inlining functions and typically fails doing so when pointers to members are involved).

Is it possible to access the arguments from an std::bind object?

std::bind(&EditorWidget::editRow, editorWidget, item)

std::bind here is creating a functional object that takes a pointer to a member function EditorWidget::editRow, bound to an object editorWidget, using the parameter item. What you've done is actually fix the parameter to the function EditorWidget::editRow with the parameter item. So effectively you've created a function object that takes no argument (since you've fixed it), and returns void.

There's actually no need for the constructor of PopupMenu to have a second parameter of type RowItem*. You could change the constructor like so:

PopupMenu::PopupMenu(std::function<void()> editRowFunction)
: _editRowFunction(editRowFunction)
{
}

and then call your function object like this:

PopupMenu::execute(} {
_editRowFunction();
}

In your current code the parameter _item is not being used by the function object you pass into the constructor PopupMenu. It satisfies the compiler since _editRowFunction is of type std::function<void(RowItem*)>.

Here's a simple example to illustrate to the point:

#include <iostream>
#include <functional>

struct callable
{
callable(std::function<void(std::string)> fn) : mFn(fn)
{}

std::function<void(std::string)> mFn;

void Run() { mFn("world"); }
};

struct Foo {
void print(std::string msg)
{
std::cout << msg << '\n';
}
};

int main()
{
Foo f;
auto fn = std::bind(&Foo::print, &f, "hello");
fn();

callable c(fn);
c.Run(); //expecting "world" to be printed
}

You might expect the output to be:

hello
world

but actually it's:

hello
hello

Live demo.

What I could do is change the definition of the function object like this:

 auto fn = std::bind(&Foo::print, &f, std::placeholders::_1); //uses a placeholder

and I get the expected output. You could do something similar without having to make many changes to your current implementation.



Related Topics



Leave a reply



Submit