【问题标题】:Set of pointer to member指向成员的指针集
【发布时间】:2014-05-15 10:27:05
【问题描述】:

这不起作用:

struct A {
    int x1;
    int x2;
};

int main() {
    int A::*p1 = &A::x1;

    set<int A::*> s;
    s.insert( p1 );  // compile error:  No operator<
    unordered_set<int A::*> us;
    us.insert( p1 ); // compile error:  No hash function 
}

我必须提供比较函数(set)或哈希函数(unordered_set)。到目前为止,我想出的唯一方法是检查成员指针下的原始字节:

struct XLess {
    bool operator()( int A::* a, int A::*b ){
        return memcmp( &a, &b, sizeof(a) ) < 0;
    }
};

set<int A::*, XLess> s; // now appears to work

这是创建集合的可靠方法吗?它取决于由相同字节表示的相同指针。有没有更好的解决方案?

【问题讨论】:

  • 假设你想要它的成员指针,set 是否重要,以至于vector 不会削减它?
  • @sp2danny 是的,这将是一种可能的解决方法。如果没有更好的结果,我会考虑。谢谢。

标签: c++ pointer-to-member


【解决方案1】:

从形式上讲,不能保证。在实践中,指向成员的指针 data 很可能只是一个简单的整数类型,而您 应该没有问题。对于指向成员函数的指针,在 另一方面:这些通常或多或少复杂 结构,通常会包含填充,其内容是 未定义,所以你的技术行不通。

我还注意到,在第一个 sn-p 中,您使用了 std::unordered_set,而不是 std::set。 std::unordered_set 不使用排序函数,而是使用等价函数 函数(并且== 是为指向成员的指针定义的)和 一个哈希函数。当然,实现哈希函数有 与执行排序相同的问题。

话虽如此:你到底为什么想要这样一套。 指向成员的指针(例如int A::*)只能指向成员 给定类型的(不是给定数组的成员 类型),并且您不可能编写具有数千个 成员。最简单的解决方案可能只是使用 std::vector&lt;int A::*&gt; 和线性搜索 (std::find) 到 确定成员资格。它可能比任何一个都快 std::set,除非你有成千上万的成员。

【讨论】:

    【解决方案2】:

    您可以扩展 std::less 和 std::hash 运算符,顺便说一句,这是完全允许的。同样受SO answer 的启发,我创建了size_t getIndexOf(int A::* x) 用于散列和比较,见下文:

    #include <set>
    #include <unordered_set>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    struct A {
      int x1;
      int x2;
    };
    
    typedef int (A::*x1);
    vector<x1> canon;
    size_t getIndexOf(int A::* x) {
      auto it = find(canon.begin(), canon.end(), x);
      if (it != canon.end()) return it - canon.begin();
      canon.push_back(x);
      return canon.size() - 1;
    }
    
    namespace std {
      template<> struct less<int A::*> {
        bool operator()(int A::* a, int A::*b){ return getIndexOf(a) < getIndexOf(b); }
      };
    
      template <> struct hash<int A::*>
      {
        size_t operator()(int A::* x) const
        {
          return  getIndexOf(x);
        }
      };
    }
    
    int main() {
      int A::*p1 = &A::x1;
      int A::*p2 = &A::x2;
      set<int A::*> s;
      s.insert(p1);
      s.insert(p2);
    
      for (auto e : s) std::cout << getIndexOf(e) << std::endl;
    
      unordered_set<int A::*> us;
      us.insert(p1);
      us.insert(p2);
    
      for (auto e : us) std::cout << getIndexOf(e) << std::endl;
    
      return 0;
    }
    

    【讨论】:

    • 这看起来不错。这是低效的,但这是意料之中的,我不在乎,而且它不是线程安全的,可以很容易地修复。谢谢。
    • 这个解决方案使得使用关联容器变得多余。您也可以只使用一个成员指针向量并首先执行线性搜索。
    【解决方案3】:

    您的 XLess 比较器依赖于标准不保证的一个假设:
    成员指针的所有位都是有效的,并且具有不同位模式的两个成员指针指向不同的成员。
    尽管如此,只要它们不指向虚函数,它应该适用于几乎任何具有平面内存模型的实现:有两种方法可以指向它们:使用 vtable,或包含函数地址。

    此外,AFAICT 没有更好的方法,因为为此您需要了解成员指针的内部结构或实现提供的比较。

    【讨论】:

    • 只要指针不指向成员函数。指向成员函数的指针必须假定它指向的函数可能是虚函数,也可能不是虚函数;一种常见的方法是使用等价的struct { void* ptrNonVirtual; int indexInVTable; }(使用空指针表示虚拟),如果指针大于int,这可能会导致填充。
    猜你喜欢
    • 2018-03-24
    • 1970-01-01
    • 2013-05-02
    • 2023-03-12
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-02
    相关资源
    最近更新 更多