【问题标题】:Comparing std::functions for equality?比较 std::functions 是否相等?
【发布时间】:2026-01-07 13:10:01
【问题描述】:

如何比较两个 C++11 std::functions 和 operator==,如果两个 functions 都引用同一个函数指针,则返回 true

【问题讨论】:

  • std::function::target是做什么的?
  • 有趣的是,boost::functionstd::function AFAIK 允许更多的平等检查。
  • 似乎与以下内容重复:*.com/questions/3629835/…
  • std::function 不必引用函数指针。无论如何,两个函数相等意味着什么?甚至在您到达 std::function 之前,这是一个可疑的操作。

标签: c++ c++11 function-pointers std-function


【解决方案1】:

operator== for std::functionstd::function 与空指针进行比较,据我所知,该标准没有提供任何详细信息为什么。

虽然,这个提升常见问题解答条目,Why can't I compare boost::function objects with operator== or operator!=? 提供了一个基本原理,据我所知,它也应该适用于std::function。引用常见问题解答:

boost::function 对象之间的比较不能“很好”实现,因此不会实现。 [...]

然后它概述了类似于 Preet 的请求解决方案并继续说:

当 f 和 g 存储的函数对象的类型没有 operator==[...] 时会出现问题

并解释为什么必须在赋值运算符或构造函数中处理这个问题,然后继续说:

所有这些问题都转化为 boost::function 构造函数或赋值运算符的失败,即使用户从未调用 operator==。我们不能对用户这样做。

更新

Accessing the target of a tr1::function object 中找到了一个标准原理,它相当古老,但与 boost FAQ 一致,并说:

operator== 在 C++ 语言中无法实现 tr1::function,因为我们没有可靠的方法来检测给定类型 T 是否在没有用户帮助的情况下是 Equality Comparable。

【讨论】:

    【解决方案2】:

    你实际上可以让它与.target一起工作:

    template<typename T, typename... U>
    size_t getAddress(std::function<T(U...)> f) {
        typedef T(fnType)(U...);
        fnType ** fnPointer = f.template target<fnType*>();
        return (size_t) *fnPointer;
    }
    
    if (getAddress(f) == getAddress(g)) {...}
    

    (参考:C++ trying to get function address from a std::function

    【讨论】:

    • 魔术,谢谢。出于某种原因,我在没有模板的情况下尝试返回了相同函数的两个 std::function 包装器的不同函数指针。好奇!
    • 将函数指针转换为size_t 有条件地支持实现定义的语义。所以这个解决方案可能不便携。
    • 这只适用于被包装的对象是一个函数指针,而不是一个可调用的类或可以被std::function包装的其他东西之一
    • 如果你知道目标的类型,那么也许你不需要std::function开头。
    【解决方案3】:

    您可以尝试比较ab,首先比较它们的.target_type(),如果这些目标类型ID 相同,那么您可以比较它们的.target() 指针。您可以使用不匹配的目标类型作为早期输出错误。

    【讨论】:

    • target&lt;T&gt;() 需要知道存储的类型,而不仅仅是知道它是相同的。
    • 你是指函数的类型?因为我觉得我真的可以做到。
    【解决方案4】:

    如果std::function&lt;T(U...)&gt; f 是成员函数,则fnPointer 将为空。

    【讨论】:

      【解决方案5】:

      请注意,函数的相等性(确定两个函数是否始终具有相同的可观察行为)在 lambda 演算中是一个无法确定的问题(这就是为什么许多编程语言禁止比较函数;或者至少是闭包;两个非常不同的函数可以具有相同的可观察行为:例如,将一个函数的 C 代码中的变量重命名为另一个函数,或者手动进行一些循环展开等...)。

      因此,即使== 测试编译,它最多也只是测试代码是否相同(具有相同的地址),而不是比较的函数具有相同的行为。

      【讨论】:

      • 你可以完美地将两个裸函数指针相互比较并得到预期的结果......我认为它与lambda caculus没有太大关系。
      • 但是比较两个闭包(或 lambda 表达式,或匿名函数)呢?
      【解决方案6】:

      比较两个 shared_ptr 怎么样?

      using MessageFilter = std::function<void(const int msgID)>;
      
      static void onMessageReceived(const int msgID)
      {
          std::cout << "msg id => " << msgID << std::endl;
      }
      
      static void someFunc()
      {
          auto filter = std::make_shared<MessageFilter>(&onMessageReceived);
      
          if (filter && *filter)
          {
              (*filter)(1234);
          }
      }
      

      如您所见,'filter' 是一个 shared_ptr,因此很容易与另一个比较。

      【讨论】:

      • 谢谢您,我们在Delegates template library 中使用了您的建议。通过这种方式,我们能够删除试图逐字节比较 std::function 的 hack 代码。您的解决方案要简单得多,并且可以立即在所有平台上运行。