【问题标题】:Is it possible to access the arguments from an std::bind object?是否可以从 std::bind 对象访问参数?
【发布时间】:2020-09-04 03:15:03
【问题描述】:

请考虑以下情况。

一个假设的弹出菜单类,它显示一些动作,当其中一个被选中时,它将调用以std::function 形式传入的动作:

PopupMenu::PopupMenu(std::function<void(RowItem*)> editRowFunction, RowItem *item)
    : _editRowFunction(editRowFunction)
    , _item(item) {
}

然后在某些时候它可能会调用执行:

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

然后我有另一个作为 UI 对象的类:

class EditorWidget {
    void editRow(RowItem *row) {
        //edit the row
    }
}

这就是我如何使用它们:

int main() {
    auto item = new RowItem();
    auto editorWidget = new EditorWidget();
    PopupMenu menu(std::bind(&EditorWidget::editRow, editorWidget, item), item);
    menu.execute();
    return 0;
}

一切正常。我的问题如下: 如果我已经在std::bind 中传递了参数item,为什么我必须再次将它作为第二个参数传递,以便能够使用该参数调用绑定函数?如果我不这样做,并尝试从 PopupMenu::execute() 单独调用该函数,我会收到编译器错误。

另一种方法是让PopupMenu的构造函数像这样:

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

如果我这样做,那么我会这样称呼它:

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

我不喜欢这种方法的地方在于,我几乎可以在PopupMenu 构造函数中传递任何绑定函数,并且它将被调用。但这不是我想要的,我只想强制执行具有特定签名的函数。

我也可以传递一个 lambda,是的。但是让我们尝试在没有 lambda 的情况下解决它。 提前感谢大家的帮助。

【问题讨论】:

  • 你研究过占位符参数吗?
  • @StephenNewell,是的,从我所见,这并没有解决我的问题。我将它绑定到一个实际的参数,所以我不需要传递一个占位符。但是,我想以占位符的形式访问该参数,但我认为它不会起作用。
  • @Scheff,是的,但我不想使用 lambda。问题是为什么不能通过std::bind做?

标签: c++ c++11 functor std-function stdbind


【解决方案1】:
std::bind(&EditorWidget::editRow, editorWidget, item)

std::bind 在这里创建一个函数对象,它接受一个指向成员函数EditorWidget::editRow 的指针,绑定到一个对象editorWidget,使用参数item。您所做的实际上是使用参数item 将参数修复为函数EditorWidget::editRow。如此有效地创建了一个不带参数的函数对象(因为您已修复它),并返回 void

PopupMenu 的构造函数实际上不需要RowItem* 类型的第二个参数。您可以像这样更改构造函数:

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

然后像这样调用你的函数对象:

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

在您当前的代码中,您传递给构造函数PopupMenu 的函数对象没有使用参数_item。它满足编译器,因为_editRowFunction 的类型为std::function&lt;void(RowItem*)&gt;

这里有一个简单的例子来说明这一点:

#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
}

您可能期望输出是:

hello
world

但实际上是:

hello
hello

直播demo

我可以做的是像这样改变函数对象的定义:

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

我得到了预期的输出。您可以做类似的事情,而不必对当前的实现进行很多更改。

【讨论】:

  • 有没有办法从fnmFn 中提取绑定参数?如果不是,这是否意味着mFn 并没有真正存储它?
  • “你可以做类似的事情,而不必对你当前的实现做很多改变。” - 我想我必须对我的实现做很多改变,因为我不能用占位符传递我的std::bind(),这对我的例子没有用。
  • 我的意思是我可以用std::placeholders::_1 传递,但是我也必须指定参数,就像我现在正在做的那样,然后用那个参数调用。我的目标不是让事情执行,更多的是在我的 API 和消费者之间执行某种类型的合同,这样消费者就不会犯错。
  • 我能否以某种方式让我的构造函数要求调用它以传入 std::placeholds::_1 而不是实际参数?
  • @armanali 我认为您甚至不需要在构造函数中添加第二个参数item,因为您的函数对象已经绑定到您的对象item。如上所述,使用std::function&lt;void()&gt;。在函数调用之间更改的参数使用占位符会更有意义。
猜你喜欢
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多