相关:what can I use as std::map
keys?
我需要创建一个映射,其中空间中的特定键位置映射到对象列表。 std::map
似乎是这样做的。
所以我在xyz std::map
Vector
class Vector
{
float x,y,z
} ;
,我正在制作std::map<Vector, std::vector<Object*> >
。所以注意这里的关键不是一个std::vector
,它是class Vector
的一个对象,它只是我自己制作的数学xyz向量。
为了产生“严格弱的排序”,我为operator<
编写了以下重载:
bool Vector::operator<( const Vector & b ) const {
// z trumps, then y, then x
if( z < b.z )
{
return true ;
}
else if( z == b.z )
{
if( y < b.y )
{
// z == b.z and y < b.y
return true ;
}
else if( y == b.y )
{
if( x < b.x )
{
return true ;
}
else if( x == b.x )
{
// completely equal
return false ;
}
else
{
return false ;
}
}
else
{
// z==b.z and y >= b.y
return false ;
}
}
else
{
// z >= b.z
return false ;
}
}
它有点长,但基本上是这样的,所以任何矢量都可以始终被认为小于任何其他矢量((-1,-1,-1)&lt;(-1,-1,1),和( -1,-1,1)&gt;(-1,-1,-1)例如)。
我的问题是这真的是人为的,虽然我已编码并且它有效但我发现它“污染”我的Vector类(数学上)这个非常奇怪,人为的,非基于数学的概念“小于“对于矢量。
但我需要创建一个映射,其中空间中的特定键位置映射到某些对象,而std :: map似乎是这样做的。
连连呢?开箱即用的解决方案欢迎!!
答案 0 :(得分:5)
您可以为地图提供自定义比较器,而不是为您的键类定义operator<
。这是一个函数对象,它接受两个参数,如果第一个参数位于第二个参数之前,则返回true
。像这样:
struct CompareVectors
{
bool operator()(const Vector& a, const Vector& b)
{
// insert comparison code from question
}
};
typedef std::map<Vector, Value, CompareVectors> VectorValueMap;
答案 1 :(得分:3)
您可以将它与班级分开。然后将其指定为std :: map的比较运算符。
std::map<Vector,std::vector<Object*>,Compare> data;
其中Compare是一个可以比较两个Vector对象的函数(或函子) 我还认为你可以简化比较操作。
bool Compare<( const Vector& lhs, const Vector& rhs)
{
// z trumps, then y, then x
if( lhs.z < rhs.z )
{ return true ;
}
else if (lhs.z > rhs.z)
{ return false;
}
// Otherwise z is equal
if( lhs.y < rhs.y )
{ return true ;
}
else if( lhs.y > rhs.y )
{ return false;
}
// Otherwise z and y are equal
if ( lhs.x < rhs.x )
{ return true;
}
/* Simple optimization Do not need this test
If this fails or succeeded the result is false.
else if( lhs.x > rhs.x )
{ return false;
}*/
// Otherwise z and y and x are all equal
return false;
}
请注意,我们测试的次数越少越好,然后等于下降。就个人而言,我喜欢这种风格的简约。但我经常看到这样被压缩:
bool Compare<( const Vector& lhs, const Vector& rhs)
{
// Note I use three separate if statements here for clarity.
// Combining them into a single statement is trivial/
//
if ((lhs.z < rhs.z) ) {return true;}
if ((lhs.z == rhs.z) && (lhs.y < rhs.y) ) {return true;}
if ((lhs.z == rhs.z) && (lhs.y == rhs.y) && (lhs.x < rhs.x)) {return true;}
return false;
}
答案 2 :(得分:1)
我认为std::tr1::unordered_map
正是您所需要的。不需要严格的弱排序。 GCC在tr1
命名空间中也有类似的东西。或者去Boost.Unordered。
行人map
或set
的无序对手为您带来两个好处:
您无需定义一个没有意义的小于运算符
散列表可能比平衡二叉树表现更好,后者是实现有序map
或set
结构的首选方法。但这取决于您的数据访问模式/要求。
所以,请继续使用:
typedef std::tr1::unordered_map<Vector, std::vector<Object *> > VectorMap;
这使用了一个默认的哈希函数,负责插入/搜索你的地图。
PS:> >
内容将在即将推出的标准中修复,因此将来的编译器版本将被修复。
答案 3 :(得分:1)
您发现您的课程受此污染是正常的。从CS的角度来看,它也受到了污染。
定义此类运算符的常规方法是通过(可能是朋友)自由函数。
然而,问自己的第一个问题是:它是否有意义。问题是您已经为您的类定义了一个方法,该方法仅在有限的上下文中有意义,但在任何地方都可访问。这就是“污染”感觉的原因。
现在,如果我需要从Vector
到Object
的集合进行此类映射,以下是我会问自己的问题:
Vector
?是的:std::map
,否:std::unordered_map
或std::tr1::unodered_map
或std::hash_map
或boost::unordered_map
。Object
?是的:boost::ptr_vector<Object>
或std::vector< std::unique_ptr<Object> >
,否:std::vector<Object*>
现在,在这两种情况下(map
和unordered_map
),我都需要一些东西来改变我的密钥。该集合提供了一个补充模板参数,该参数采用Functor类型。
注意:正如在另一个答案中提到的,浮点表示在计算机中很笨拙,因此您可能需要放松相等的含义并忽略低位数(多少取决于您的计算)。
答案 4 :(得分:0)
我认为你的方法很好。如果您担心污染Vector类,那么我相信一个独立的功能也会起作用:
bool operator<( const Vector& lhs, const Vector& rhs )
{
...
}
但只是一句警告:你在这里做的事情风险很大。浮点计算经常出错。假设您在地图中插入了一些点。然后你计算一个点并检查地图,看它是否在那里。即使从严格的数学观点来看,第二点与第一点相同,也无法保证你会在地图中找到它。