代码之家  ›  专栏  ›  技术社区  ›  code

std::函数说明

  •  5
  • code  · 技术社区  · 6 年前

    目的是什么 std::function ? 据我所知, std::函数 将函数、函子或lambda转换为函数对象。

    std::函数

    我也不明白为什么要把一个正则函数变成一个函数对象。如果我想使用一个函数对象,我首先会做一个作为函子或lambda的函数对象。。。而不是编写一个函数,然后用std::function转换它,然后作为谓词传入。。。

    std::函数 ... 乍一看不太明显的东西。

    对…的解释 std::函数 非常感谢。

    6 回复  |  直到 6 年前
        1
  •  10
  •   Barry    6 年前

    目的是什么 std::function ? 据我所知, std::函数

    std::函数 是一个被称为类型擦除的更广泛概念的例子。你的描述不太准确。什么 std::function<void()> 任何 可调用的,无需参数即可调用的。它可以是函数指针或具有具体类型的函数对象,也可以是从lambda构建的闭包。源类型是什么并不重要,只要它符合合同就行了。我们不使用具体的源类型,而是“擦除”它-我们只处理 std::函数 .

    有时,不能使用具体类型。一个更常见的例子是常规的面向对象多态性。为什么我们要储存 Base* Derived* ? 好吧,也许我们不能储存 派生的* . 也许我们有很多不同 派生的* Derived . 这是 std::函数 使用。

    用例的非穷尽列表:

    • std::函数 std::vector<std::function<void()>> callbacks -可能都有不同的具体类型,但我不在乎,我只需要打电话给他们。
    • 需要跨API边界使用(例如,我可以 virtual 函数获取 std::函数<void()> ,但我不能 事实上的
    • 从工厂函数返回-我们只需要满足某些概念的对象,不需要具体的东西(同样,在OO多态性中非常常见,这也是类型擦除)。
    • 可能在任何地方都实际使用模板,但是性能的提高不值得编译。
        2
  •  4
  •   bipll    6 年前

    考虑一个简单的用例:

    /* Unspecified */ f = [](int x, int y){ return x + y; };
    f = [](int x, int y){ return x - y; };
    int a = 42;
    f = [&a](int x, int y){ return a * x * y; };
    

    您将如何指定 /* Unspecified */ ?

    此外,

    std::queue<of what?> jobs;
    jobs.push_back([]{ std::cout << "Hi!\n"; });
    jobs.push_back([]{ std::cout << "Bye!\n"; });
    for(auto const &j: jobs) j();
    

    value_type 应该放在里面 jobs ?

    最后,

    myButton.onClick(f);
    

    f 有吗?模板参数?好吧,但是内部是怎么登记的呢?

        3
  •  1
  •   Drax    6 年前

    据我所知,std::function将函数、函子或lambda转换为函数对象。

    你总结一下,你可以 任何 把这些变成同一个东西,一个 std::function ,然后可以根据需要存储和使用。

    一般来说,当你在设计一个类或一个API时,你通常没有理由将你的特性限制在这些特性中的一个,所以使用 std::函数 允许API的用户自由选择,而不是强制用户选择一种特定类型。 您甚至可以将这些类型的不同形式存储在一起,它基本上是具有给定签名和明确定义的语义的可调用类型的抽象。

        4
  •  1
  •   Pete Becker    6 年前

    在我见过的大多数用途中, std::function

    首先,它为调用函数对象提供了统一的语法。例如,可以使用 实例化来包装一个普通函数,该函数接受一个类类型或成员函数的单个参数以及它应该应用到的类对象,而不必担心不同的调用语法。

    struct S {
        void f();
    };
    
    void g(const S&);
    
    S obj;
    
    typedef std::function<void()> functor1(&S::f, obj);
    typedef std::function<void()> functor2(&g, obj);
    
    functor1(); // calls obj.f()
    functor2(); // calls g(obj);
    

    注意这里的两个函子都是用相同的语法调用的。在编写泛型代码时,这是一个很大的好处。如何调用底层函数的决定是在 std::函数 模板,而不必在代码中找到它。

    另一个好处是,您可以重新分配 对象保存:

    functor1 = std::function<void>()>(&g, obj);
    

    这会改变 functor1 :

    functor1() // calls g(obj)
    

    有时候这很重要。

        5
  •  1
  •   Daniel Schepler    6 年前

    std::function 在实现“观察者模式”时非常有用。例如,假设您想要实现一个简单的“表达式求值器”计算器GUI。要对使用observer模式针对GUI库编写的代码类型给出某种抽象的概念,请执行以下操作:

    class ExprEvalForm : public GuiEditorGenerated::ExprEvalForm {
    public:
        ExprEvalForm() {
            calculateButton.onClicked([] {
                auto exprStr = exprInputBox.get();
                auto value = ExprEvaluator::evaluate(exprStr);
                evalOutputLabel.set(std::to_string(value));
            });
        }
    };
    

    现在,GUI库的button类如何存储传递给 onClicked 一吻 std::函数 可以发挥作用。因此,button类实现的框架可能如下所示:

    class PushButton : public Widget {
    public:
        using ButtonClickedCallback = std::function<void()>;
        void onClicked(ButtonClickedCallback cb) {
            m_buttonClickedCallback = std::move(cb);
        }
    
    protected:
        void mouseUpEvent(int x, int y) override {
            ...
            if (mouseWasInButtonArea(x, y))
                notifyClicked();
            ...
        }
    
    private:
        void notifyClicked() {
            if (m_buttonClickedCallback)
                m_buttonClickedCallback();
        }
        ButtonClickedCallback m_buttonClickedCallback;
    };
    
        6
  •  0
  •   Mateusz Wojtczak    6 年前

    在实现线程池时,使用函数对象很有帮助。您不能将任何可用的工作线程保留为线程,而将要做的工作保留为函数对象的队列。保持工作作为函数对象比函数指针更容易,例如,您可以传递任何可调用的内容。每当新的函数对象出现在队列中时,工作线程就可以弹出它并通过调用它的()操作符来执行。