【问题标题】:Function objects in C++ (C++11)C++ (C++11) 中的函数对象
【发布时间】:2012-10-10 13:57:22
【问题描述】:

我正在阅读有关 boost::function 的信息,我对它的使用以及它与我在文档中找到的其他 C++ 结构或术语的关系感到有些困惑,例如here.

在 C++ (C++11) 的上下文中,boost::function 的实例、函数对象函子lambda 表达式?什么时候应该使用哪个构造?例如,何时应该将函数对象包装在 boost::function 中,而不是直接使用该对象?

以上所有的 C++ 是否构造了不同的方式来实现函数式语言中称为 closure(一个函数,可能包含捕获的变量,可以作为值传递并由其他函数调用)?

【问题讨论】:

    标签: function boost c++11 functional-programming functor


    【解决方案1】:

    函数对象和函子是一回事;实现函数调用运算符operator() 的对象。一个 lambda 表达式产生一个函数对象。具有boost::function/std::function 的某些特化类型的对象也是函数对象。

    Lambda 的特殊之处在于 lambda 表达式具有匿名且唯一的类型,并且是创建内联函子的便捷方式。

    boost::function/std::function 的特殊之处在于它将任何可调用实体转换为函子,其类型仅取决于可调用实体的签名。例如,每个 lambda 表达式都有一个唯一的类型,因此很难将它们传递给非泛型代码。如果您从 lambda 创建 std::function,那么您可以轻松地传递包装的 lambda。

    【讨论】:

    • 那么boost::function是用来定义函数对象的签名的?
    • @Giorgio 签名但不是它的类型。
    • @Giorgio 不定义签名,而是隐藏签名以外的所有内容。它还可以方便地将非对象可调用实体作为对象处理。
    • “例如,每个 lambda 表达式都有一个唯一的类型”:这是否意味着具有相同签名的两个 lambda 表达式(例如,(int x, int y) -> int)具有不同的类型?
    • @Giorgio 不仅如此,即使两个 lambda 表达式中的确切标记序列相同,类型也是唯一的:static_assert(!std::is_same<decltype([]{}),decltype([]{})>::value,"these two lambda expressions have different types!");
    【解决方案2】:

    boost::function 和标准版std::function 都是库提供的包装器。它们可能很昂贵且相当重,只有在您确实需要一个异构的、可调用实体的集合时才应该使用它们。只要您一次只需要一个可调用实体,您最好使用auto 或模板。

    这是一个例子:

    std::vector<std::function<int(int, int)>> v;
    
    v.push_back(some_free_function);           // free function
    v.push_back(&Foo::mem_fun, &x, _1, _2);    // member function bound to an object
    v.push_back([&](int a, int b) -> int { return a + m[b]; });  // closure
    
    int res = 0;
    for (auto & f : v) { res += f(1, 2); }
    

    这是一个反例:

    template <typename F>
    int apply(F && f)
    {
        return std::forward<F>(f)(1, 2);
    }
    

    在这种情况下,像这样声明apply 是完全没有必要的:

    int apply(std::function<int(int,int)>)   // wasteful
    

    转换是不必要的,模板化版本可以匹配实际(通常是不可知的)类型,例如绑定表达式或 lambda 表达式。

    【讨论】:

      【解决方案3】:

      Function Objects 和 Functors 通常被描述为 概念。这意味着它们描述了一种类型的一组需求。一种 在 C++11 和新的 概念称为Callable。可调用类型的对象o 是 对象,其中(基本上)表达式o(ARGS) 为真。例子 对于Callable

      int f() {return 23;}
      
      struct FO {
        int operator()() const {return 23;}
      };
      

      通常会增加一些对Callable的返回类型的要求 也。你像这样使用Callable

      template<typename Callable>
      int call(Callable c) {
        return c();
      }
      
      call(&f);
      call(FO());
      

      像上面这样的构造需要你知道确切的类型 编译时。这并不总是可能的,这就是 std::function进来。

      std::function 就是这样一个Callable,但它可以让你擦除 您正在调用的实际类型(例如,您的函数接受可调用 不再是模板)。仍然调用一个函数需要你 知道它的参数和返回类型,因此必须将它们指定为 std::function 的模板参数。

      你会这样使用它:

      int call(std::function<int()> c) {
        return c();
      }
      
      call(&f);
      call(FO());
      

      您需要记住,使用 std::function 可能会影响 性能,您应该只在确定需要时使用它 它。在几乎所有其他情况下,模板都能解决您的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-26
        • 2012-11-20
        • 1970-01-01
        • 2023-03-29
        • 1970-01-01
        • 1970-01-01
        • 2019-07-14
        • 2014-01-18
        相关资源
        最近更新 更多