指向成员的指针集
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::set
。 std::unordered_set
不使用排序函数,而是使用等价函数
函数(并且==
是为指向成员的指针定义的)和
一个哈希函数。当然,实现哈希函数有
与执行排序相同的问题。
话虽如此:你到底为什么想要这样一套。
指向成员的指针(例如int A::*
)只能指向成员
给定类型的(不是给定数组的成员
类型),并且您不可能编写具有数千个
成员。最简单的解决方案可能只是使用
std::vector<int A::*>
和线性搜索 (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
,这可能会导致填充。以上是关于指向成员的指针集的主要内容,如果未能解决你的问题,请参考以下文章