- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在尝试散列一个 Edge 结构,以便我可以拥有一个具有唯一边的 unordered_set。在我的例子中,如果一条边的两个端点的组合在之前的集合中没有遇到,则该边被认为是唯一的。
虽然我的代码适用于仅包含 Edge 类型的 unordered_set,但我无法让它适用于指向 Edge 类型的指针。请在下面查看我有点冗长的代码。非常感谢任何帮助。
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <unordered_set>
struct Vector3
{
float x, y, z;
Vector3() {}
Vector3(float xx, float yy, float zz)
{
x = xx, y = yy, z = zz;
}
inline bool operator==(const Vector3& other) const
{
return x == other.x && y == other.y && z == other.z;
}
friend std::ostream& operator<<(std::ostream& stream, const Vector3& vector);
};
std::ostream& operator<<(std::ostream& stream, const Vector3& vector)
{
return stream
<< "("
<< std::setw(2) << std::setfill(' ') << vector.x << ", "
<< std::setw(2) << std::setfill(' ') << vector.y << ", "
<< std::setw(2) << std::setfill(' ') << vector.z
<< ")";
}
struct Edge
{
Vector3 EndPoints[2];
Edge() {}
Edge(Vector3 p, Vector3 q)
{
EndPoints[0] = p;
EndPoints[1] = q;
}
inline bool operator==(const Edge& other) const
{
return (EndPoints[0] == other.EndPoints[0] || EndPoints[0] == other.EndPoints[1]) &&
(EndPoints[1] == other.EndPoints[1] || EndPoints[1] == other.EndPoints[0]);
}
inline bool operator==(const Edge* other) const
{
return (EndPoints[0] == other->EndPoints[0] || EndPoints[0] == other->EndPoints[1]) &&
(EndPoints[1] == other->EndPoints[1] || EndPoints[1] == other->EndPoints[0]);
}
friend std::ostream& operator<<(std::ostream& stream, const Edge& vector);
friend std::ostream& operator<<(std::ostream& stream, const Edge* vector);
};
std::ostream& operator<<(std::ostream& stream, const Edge& edge)
{
return stream << edge.EndPoints[0] << " -- " << edge.EndPoints[1];
}
std::ostream& operator<<(std::ostream& stream, const Edge* edge)
{
return stream << edge->EndPoints[0] << " -- " << edge->EndPoints[1];
}
namespace std
{
template <>
struct hash<Edge>
{
std::size_t operator()(const Edge& edge) const
{
Vector3 p0 = edge.EndPoints[0];
Vector3 p1 = edge.EndPoints[1];
if (p1.x < p0.x) std::swap(p0.x, p1.x);
if (p1.y < p0.y) std::swap(p0.y, p1.y);
if (p1.z < p0.z) std::swap(p0.z, p1.z);
unsigned hash0 = (int(p0.x*73856093) ^ int(p0.y*19349663) ^ int(p0.z*83492791)) % 1024;
unsigned hash1 = (int(p1.x*73856093) ^ int(p1.y*19349663) ^ int(p1.z*83492791)) % 1024;
return hash0 ^ (hash1 << 3);
}
};
template <>
struct hash<Edge*>
{
std::size_t operator()(const Edge* edge) const
{
Vector3 p0 = edge->EndPoints[0];
Vector3 p1 = edge->EndPoints[1];
if (p1.x < p0.x) std::swap(p0.x, p1.x);
if (p1.y < p0.y) std::swap(p0.y, p1.y);
if (p1.z < p0.z) std::swap(p0.z, p1.z);
unsigned hash0 = (int(p0.x*73856093) ^ int(p0.y*19349663) ^ int(p0.z*83492791)) % 1024;
unsigned hash1 = (int(p1.x*73856093) ^ int(p1.y*19349663) ^ int(p1.z*83492791)) % 1024;
std::size_t key = hash0 ^ (hash1 << 3);
return key;
}
};
}
void add_edge(std::unordered_set<Edge>& table, Edge edge)
{
std::unordered_set<Edge>::const_iterator it = table.find(edge);
if (it == table.end()) table.insert(edge);
else std::cout << "Table already contains edge " << edge << std::endl;
}
void add_edge(std::unordered_set<Edge*>& table, Edge* edge)
{
std::unordered_set<Edge*>::const_iterator it = table.find(edge);
if (it == table.end()) table.insert(edge);
else std::cout << "Table already contains edge " << edge << std::endl;
}
void print_table(std::unordered_set<Edge>& table)
{
std::cout << std::endl;
std::cout << "Table has " << table.size() << " elements:" << std::endl;
for (auto it = table.begin(); it != table.end(); ++it)
std::cout << *it << std::endl;
std::cout << std::endl;
}
void print_table(std::unordered_set<Edge*>& table)
{
std::cout << std::endl;
std::cout << "Table has " << table.size() << " elements:" << std::endl;
for (auto it = table.begin(); it != table.end(); ++it)
std::cout << *(*it) << std::endl;
std::cout << std::endl;
}
int main()
{
std::unordered_set<Edge> table;
std::unordered_set<Edge*> ptable;
Edge e0(Vector3( 1.f, 0.f, 0.f), Vector3(-1.f, 0.f, 0.f));
Edge e1(Vector3(-1.f, 0.f, 0.f), Vector3( 1.f, 0.f, 0.f));
add_edge(table, e0);
add_edge(table, e1);
print_table(table);
add_edge(ptable, &e0);
add_edge(ptable, &e1);
print_table(ptable);
return 0;
}
这是输出:
1> Table already contains edge (-1, 0, 0) -- ( 1, 0, 0)
1>
1> Table has 1 elements:
1> ( 1, 0, 0) -- (-1, 0, 0)
1>
1> Table has 2 elements:
1> (-1, 0, 0) -- ( 1, 0, 0)
1> ( 1, 0, 0) -- (-1, 0, 0)
所以我的问题是:第二个元素为什么要添加到第二个表中?我已经检查了哈希函数,但它为两个条目返回相同的键,所以这似乎不是罪魁祸首,但我不确定它可能是什么。
编辑:
我现在发现 inline bool operator==(const Edge* other) const
没有被调用,但我不确定为什么。
最佳答案
Angew 指出了真正的问题。
还有其他问题。似乎您希望 Edge 始终是双向的,因此 Edge(a,b) == Edge(b,a)。
Side Note The best way (IMO) to achieve this, is to order the end-points in deterministic order during
Edge
construction. No need to think about it later. This is called an invariant and removes the burden to check for 'equivalence' of Edges throughout all the rest of the code.
但是,您的哈希函数没有正确实现这一点
你的 hash<>::operator()
阅读:
std::size_t operator()(const Edge& edge) const
{
Vector3 p0 = edge.EndPoints[0];
Vector3 p1 = edge.EndPoints[1];
if (p1.x < p0.x) std::swap(p0.x, p1.x);
if (p1.y < p0.y) std::swap(p0.y, p1.y);
if (p1.z < p0.z) std::swap(p0.z, p1.z);
unsigned hash0 = (int(p0.x*73856093) ^ int(p0.y*19349663) ^ int(p0.z*83492791)) % 1024;
unsigned hash1 = (int(p1.x*73856093) ^ int(p1.y*19349663) ^ int(p1.z*83492791)) % 1024;
return hash0 ^ (hash1 << 3);
}
这种交换逻辑有效地构成了伪造的端点。
Edge(ep[3,1,2], ep[1,2,3])
变成 Edge(ep[1,1,2], ep[3,2,3])
你可能想要的地方 Edge(ep[1,2,3], ep[3,1,2])
.
修复它会交换整个端点,而不是单个 vector 元素:
if (std::tie(p1.x, p1.y, p1.z) < std::tie(p0.x, p0.y, p0.z)) {
using std::swap;
swap(p0, p1);
}
通过删除(所有)不必要的重复代码来修复哈希函数:
template <> struct hash<Edge>
{
std::size_t operator()(const Edge& edge) const {
Vector3 p0 = edge.EndPoints[0];
Vector3 p1 = edge.EndPoints[1];
if (std::tie(p0.x, p0.y, p0.z) <
std::tie(p1.x, p1.y, p1.z)) // consider`Vector3::operator<`
{
using std::swap;
swap(p0, p1);
}
auto hash_p = [](Vector3 const& p) { return (unsigned(p.x*73856093u) ^ unsigned(p.y*19349663u) ^ unsigned(p.z*83492791u)) % 1024u; };
return hash_p(p0) ^ (hash_p(p1) << 3);
}
};
指针哈希变成了一个简单的转发:
template <> struct hash<Edge*> {
std::size_t operator()(const Edge* edge) const {
return hash<Edge>()(*edge);
}
};
考虑将比较移至 Vector3::operator<
实现上述内容,并修复 Edge* 缺失的平等比较器:
#include <iostream>
#include <iomanip>
#include <unordered_set>
#include <cassert>
#include <tuple>
struct Vector3
{
float x, y, z;
Vector3() {}
Vector3(float xx, float yy, float zz)
{
x = xx, y = yy, z = zz;
}
inline bool operator==(const Vector3& other) const
{
return x == other.x && y == other.y && z == other.z;
}
inline bool operator<(const Vector3& other) const
{
return std::tie(x, y, z) < std::tie(other.x, other.y, other.z);
}
friend std::ostream& operator<<(std::ostream& stream, const Vector3& vector);
};
std::ostream& operator<<(std::ostream& stream, const Vector3& vector)
{
return stream
<< "("
<< std::setw(2) << std::setfill(' ') << vector.x << ", "
<< std::setw(2) << std::setfill(' ') << vector.y << ", "
<< std::setw(2) << std::setfill(' ') << vector.z
<< ")";
}
struct Edge
{
Vector3 EndPoints[2];
Edge() {}
Edge(Vector3 p, Vector3 q)
{
// swap order
if (q < p) { using std::swap; swap(p, q); } // the invariant
EndPoints[0] = p;
EndPoints[1] = q;
}
inline bool operator==(const Edge& other) const {
return std::tie(EndPoints[0], EndPoints[1]) == std::tie(other.EndPoints[0], other.EndPoints[1]);
}
friend std::ostream& operator<<(std::ostream& stream, const Edge& vector);
friend std::ostream& operator<<(std::ostream& stream, const Edge* vector);
};
std::ostream& operator<<(std::ostream& stream, const Edge& edge)
{
return stream << edge.EndPoints[0] << " -- " << edge.EndPoints[1];
}
std::ostream& operator<<(std::ostream& stream, const Edge* edge)
{
return stream << edge->EndPoints[0] << " -- " << edge->EndPoints[1];
}
namespace std
{
template <> struct hash<Edge>
{
std::size_t operator()(const Edge& edge) const {
assert(edge.EndPoints[0] < edge.EndPoints[1]); // the invariant
auto hash_p = [](Vector3 const& p) { return (unsigned(p.x*73856093u) ^ unsigned(p.y*19349663u) ^ unsigned(p.z*83492791u)) % 1024u; };
return hash_p(edge.EndPoints[0]) ^ (hash_p(edge.EndPoints[1]) << 3);
}
};
template <> struct hash<Edge*> {
std::size_t operator()(const Edge* edge) const {
return hash<Edge>()(*edge);
}
};
}
struct EdgePtrEqual {
bool operator()(Edge const* a, Edge const* b) const {
return *a == *b;
}
};
using EdgeSet = std::unordered_set<Edge, std::hash<Edge>>;
using EdgePtrSet = std::unordered_set<Edge*, std::hash<Edge*>, EdgePtrEqual>;
void add_edge(EdgeSet& table, Edge edge)
{
EdgeSet::const_iterator it = table.find(edge);
if (it == table.end()) table.insert(edge);
else std::cout << "Table already contains edge " << edge << std::endl;
}
void add_edge(EdgePtrSet& table, Edge* edge)
{
EdgePtrSet::const_iterator it = table.find(edge);
if (it == table.end()) table.insert(edge);
else std::cout << "Table already contains edge " << edge << std::endl;
}
void print_table(EdgeSet& table)
{
std::cout << std::endl;
std::cout << "Table has " << table.size() << " elements:" << std::endl;
for (auto it = table.begin(); it != table.end(); ++it)
std::cout << *it << std::endl;
std::cout << std::endl;
}
void print_table(EdgePtrSet& table)
{
std::cout << std::endl;
std::cout << "Table has " << table.size() << " elements:" << std::endl;
for (auto it = table.begin(); it != table.end(); ++it)
std::cout << *(*it) << std::endl;
std::cout << std::endl;
}
int main()
{
EdgeSet table;
EdgePtrSet ptable;
Edge e0(Vector3( 1.f, 0.f, 0.f), Vector3(-1.f, 0.f, 0.f));
Edge e1(Vector3(-1.f, 0.f, 0.f), Vector3( 1.f, 0.f, 0.f));
add_edge(table, e0);
add_edge(table, e1);
print_table(table);
add_edge(ptable, &e0);
add_edge(ptable, &e1);
print_table(ptable);
return 0;
}
关于c++ - unordered_set 的散列自定义指针类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18635145/
我刚接触 C 语言几周,所以对它还很陌生。 我见过这样的事情 * (variable-name) = -* (variable-name) 在讲义中,但它到底会做什么?它会否定所指向的值吗? 最佳答案
我有一个指向内存地址的void 指针。然后,我做 int 指针 = void 指针 float 指针 = void 指针 然后,取消引用它们以获取值。 { int x = 25; vo
我正在与计算机控制的泵进行一些串行端口通信,我用来通信的 createfile 函数需要将 com 端口名称解析为 wchar_t 指针。 我也在使用 QT 创建一个表单并获取 com 端口名称作为
#include "stdio.h" #include "malloc.h" int main() { char*x=(char*)malloc(1024); *(x+2)=3; --
#include #include main() { int an_int; void *void_pointer = &an_int; double *double_ptr = void
对于每个时间步长,我都有一个二维矩阵 a[ix][iz],ix 从 0 到 nx-1 和 iz 从 0 到 nz-1。 为了组装所有时间步长的矩阵,我定义了一个长度为 nx*nz*nt 的 3D 指针
我有一个函数,它接受一个指向 char ** 的指针并用字符串填充它(我猜是一个字符串数组)。 *list_of_strings* 在函数内部分配内存。 char * *list_of_strings
我试图了解当涉及到字符和字符串时,内存分配是如何工作的。 我知道声明的数组的名称就像指向数组第一个元素的指针,但该数组将驻留在内存的堆栈中。 另一方面,当我们想要使用内存堆时,我们使用 malloc,
我有一个 C 语言的 .DLL 文件。该 DLL 中所有函数所需的主要结构具有以下形式。 typedef struct { char *snsAccessID; char *
我得到了以下数组: let arr = [ { children: [ { children: [], current: tru
#include int main(void) { int i; int *ptr = (int *) malloc(5 * sizeof(int)); for (i=0;
我正在编写一个程序,它接受一个三位数整数并将其分成两个整数。 224 将变为 220 和 4。 114 将变为 110 和 4。 基本上,您可以使用模数来完成。我写了我认为应该工作的东西,编译器一直说
好吧,我对 C++ 很陌生,我确定这个问题已经在某个地方得到了回答,而且也很简单,但我似乎找不到答案.... 我有一个自定义数组类,我将其用作练习来尝试了解其工作原理,其定义如下: 标题: class
1) this 指针与其他指针有何不同?据我了解,指针指向堆中的内存。如果有指向它们的指针,这是否意味着对象总是在堆中构造? 2)我们可以在 move 构造函数或 move 赋值中窃取this指针吗?
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: C : pointer to struct in the struct definition 在我的初学者类
我有两个指向指针的结构指针 typedef struct Square { ... ... }Square; Square **s1; //Representing 2D array of say,
变量在内存中是如何定位的?我有这个代码 int w=1; int x=1; int y=1; int z=1; int main(int argc, char** argv) { printf
#include #include main() { char *q[]={"black","white","red"}; printf("%s",*q+3); getch()
我在“C”类中有以下函数 class C { template void Func1(int x); template void Func2(int x); }; template void
我在64位linux下使用c++,编译器(g++)也是64位的。当我打印某个变量的地址时,例如一个整数,它应该打印一个 64 位整数,但实际上它打印了一个 48 位整数。 int i; cout <<
我是一名优秀的程序员,十分优秀!