【问题标题】:How to hash and compare a pointer-to-member-function?如何散列和比较指向成员函数的指针?
【发布时间】:2010-11-22 15:07:02
【问题描述】:

我如何散列(std::tr1::hash 或 boost::hash)一个 c++ 指向成员函数的指针?

例子:

我有几个 bool (Class::*functionPointer)()(非静态)指向类 Class 的几个不同方法,我需要散列这些指向成员函数的指针。

我该怎么做?

另外我如何比较(std::less)那些成员函数指针,以便我可以将它们存储在 std::set 中?

【问题讨论】:

  • 通常没有任何理由对指针进行哈希处理,因为它直接指向您要访问的内容。请提供一些代码来说明您所询问的内容。
  • 你什么时候会说一个函数指针比另一个函数指针“少”?
  • @bojan:如果比较的唯一目的是将它们存储在排序列表中,则任何确定性排序都可以。例如二进制值。
  • 我有一个类,它有一个成员函数指针作为成员变量。我需要将该类存储在 std::set 和 std::hash_set 中,因此它需要该成员函数指针上的哈希和 std::less。
  • 除了成员函数指针之外,您的类实例是否在每个字段中都相同?如果没有,那么您不需要将它包含在哈希/比较中,这巧妙地避免了这个问题。

标签: c++ hash function-pointers member-function-pointers


【解决方案1】:

所有 C++ 对象,包括指向成员函数的指针,在内存中都表示为一个字符数组。所以你可以试试:

bool (Class::*fn_ptr)() = &Class::whatever;
const char *ptrptr = static_cast<const char*>(static_cast<const void*>(&fn_ptr));

现在将ptrptr 视为指向(sizeof(bool (Class::*)())) 字节数组,并对这些字节进行散列或比较。如果您愿意,可以使用unsigned char 代替char

这保证没有误报 - 在 C++03 中,指向成员函数的指针是 POD,这意味着它们可以使用 memcpy 复制。这意味着如果具有相同的逐字节值,则它们是相同的。

问题在于成员函数指针的存储表示可能包含不参与值的位 - 因此对于指向同一成员函数的不同指针,它们不一定相同。或者编译器可能出于某种模糊的原因,有不止一种方式指向同一个类的同一个函数,它们在字节上不相等。无论哪种方式,您都可能得到假阴性。您必须研究成员函数指针如何在您的实现中实际工作。它必须以某种方式为成员函数指针实现operator==,如果你能找出方法,那么你可能会找出一个顺序和一个散列函数。

这可能很难:成员函数指针很笨拙,并且根据指向的函数类型(虚拟,继承),存储可能包含不同数量的非参与“松弛空间”。因此,您可能必须与编译器的实现细节进行大量交互。本文可能会帮助您入门:http://www.codeproject.com/KB/cpp/FastDelegate.aspx

更简洁的替代方法可能是通过数组进行线性搜索,以便“规范化”所有函数指针,然后根据该函数指针在数组中的“规范”实例的位置进行比较和散列。取决于你的性能要求是什么。而且即使有需求,类(及其派生类)是否有这么多的功能,以至于线性搜索要花那么长时间?

typedef bool (Class::*func)();
vector<func> canon;

size_t getIndexOf(func fn_ptr) {
    vector<func>::iterator it = find(canon.begin(), canon.end(), fn_ptr);
    if (it != canon.end()) return it - canon.begin();
    canon.push_back(func);
    return canon.size() - 1;
}

【讨论】:

  • 感谢 char* 成功了!只有在我的编译器中,我才需要 reinterpret_cast 而不是 static_cast。
  • 对一些棘手问题的出色处理,+1。我没有想到 pmf1 == pmf2 并不一定意味着按位标识。
  • 指向成员函数的指针可能包含填充,在比较相等时将被忽略,并且可能采用随机值。散列任何填充字节将导致散列函数失败。
  • @James:我从“问题是成员函数指针的存储表示可能包含不参与值的位”开始讨论这个问题
  • 虽然带有“canoicalised”索引的想法当然很聪明(感谢这个想法!),但我想添加一个警告:据我所知,不可能进行相等比较指向虚函数的成员指针(例如接口/ABC 中的函数)。由于相同的“偏移量”实际上可能会解析为不同的实现,具体取决于您绑定成员指针的实际实例。因此,这种方法在这种情况下就失效了。
【解决方案2】:

我无法如上一个答案中所述强制转换指针(在 Microsoft 编译器 2010 中),但这对我有用:

static string fmptostr(int atype::*opt)
  {
      char buf[sizeof(opt)];
      memcpy(&buf,&opt,sizeof(opt));
      return string(buf,sizeof(opt));
  }

关于指针的按位标识,它可以是按位的,因此如果使用了适当的编译器开关,它似乎是。至少这对于 Microsoft 编译器是正确的,例如 使用#pragma pointers_to_members 还有一个开关.../vmg

【讨论】:

    【解决方案3】:

    如果您的成员函数指针是唯一的,在大多数情况下对于基于回调的订阅都是如此,那么您可以使用带有type_index 的刻度,该唯一性由类型的唯一性(即Class::Method)保证你的程序,适合存放在unordered_map,即

    struct MyEvent {
    
        using fn_t = std::function<void(MyEvent &)>;
        using map_t = std::unordered_map<std::type_index, fn_t>;
    
    
        template <typename Handler>
        void subscribe(Object& obj, Handler&& handler) {
            fn_t fn = [&, handler = std::move(handler)](MyEvent& event) {
                (obj.*handler)(event);
            }
            std::type_index index = typeid(Handler);
            subscribers.emplace(std::move(index), std::move(fn));
        }
    
        void fire() {
            for(auto& pair: subscribers) {
                auto& fn = pair.second;
                fn(*this);
            }
        }
    
        map_t subscribers;
    }
    

    以及订阅和触发事件示例:

    MyEvent event;
    MyObject obj = ...;
    event.subscribe(obj, &MyObject::on_event );
    ...
    event.fire();
    

    所以,上面的例子给你类/方法唯一性,如果你需要对象/方法唯一性,那么你应该有一个结构,它提供组合哈希,假设有std::hash&lt;MyObject&gt;并且已经有std::hash&lt;std::type_index&gt;成员函数指针。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多