指向成员的指针集

Posted

技术标签:

【中文标题】指向成员的指针集【英文标题】: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 是的,这将是一种可能的解决方法。如果没有更好的结果,我会考虑。谢谢。 【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

您可以扩展 std::lessstd::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,这可能会导致填充。

以上是关于指向成员的指针集的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB复制集成员及状态转换

Go编程基础-学习2

C++指向对象成员的指针

C++指向对象成员的指针

Go 语言之 struct 结构体

c++怎样将指向【成员函数】的指针强转成指向【普通函数】的指针