Please consider the following scenario.
A hypothetical pop-up menu class that displays some actions, and when one of them is selected it will call the passed in action which is in form of an std::function
:
PopupMenu::PopupMenu(std::function<void(RowItem*)> editRowFunction, RowItem *item)
: _editRowFunction(editRowFunction)
, _item(item) {
}
Then at some point it might call execute:
PopupMenu::execute(} {
_editRowFunction(_item);
}
Then I have this other class that's a UI object:
class EditorWidget {
void editRow(RowItem *row) {
//edit the row
}
}
And here's how I am using all of them:
int main() {
auto item = new RowItem();
auto editorWidget = new EditorWidget();
PopupMenu menu(std::bind(&EditorWidget::editRow, editorWidget, item), item);
menu.execute();
return 0;
}
Everything works. My question is the following:
If I am already passing the argument item
in std::bind
, why do I have to pass it again as a second parameter in order to be able to call the bound function with that argument? If I don't, and try to call the function just by itself from PopupMenu::execute()
, I get a compiler error.
The other way around it is to make the constructor of PopupMenu
like this:
PopupMenu::PopupMenu(std::function<void()> editRowFunction)
: _editRowFunction(editRowFunction) {}
And if I do it that way then I call it this way:
PopupMenu::execute() {
_editRowFunction();
}
What I don't like about this method is that I can pretty much pass any bound function in the PopupMenu
constructor and it will be called. But that's not what I want, I want to enforce only a function with a specific signature.
I can also pass a lambda, yes. But let's try to solve it without lambdas. Thank you all in advance for your help.
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.