简体   繁体   English

如果没有逻辑方法定义比较运算符,如何将自定义类用作std :: map的键?

[英]How to use a custom class as key with std::map if there is no logical way to have a comparison operator defined?

I'm trying to use std::map with a custom class and in the course of the process the program has to call std::map::find in order to resolve a key to a pair. 我正在尝试将std :: map与自定义类一起使用,并且在此过程中,程序必须调用std :: map :: find才能将密钥解析为一对。 The custom class doesn't seem to fit well in terms of comparisons. 自定义类在比较方面似乎不太适合。

This is probably better explained in code; 这可能在代码中更好地解释; I have a class that I want to use as a key: 我有一个要用作键的类:

 class index_t
    {
       int vertex_index;
       int normal_index;
       int texture_index;
     }

     std::map<index_t, int> reindexer;

I would like to use 我想用

   reindexer.find(index_to_find);

In order to find a key with exactly same parameters (exactly same vertex/normal/texture indices) exists in the map already. 为了找到具有完全相同参数(完全相同的顶点/法线/纹理索引)的键,该映射已经存在。

So technically I want the std::map::find function to behave like this: 所以从技术上讲,我希望std :: map :: find函数的行为如下:

bool find(key_to_find) //this is what I'm expecting from a find function of std::map
{
    if(existing_key.vertex == key_to_find.vertex && existing_key.texture == key_to_find.texture && existing_key.normal == key_to_find.normal)
         return true;
    else return false;
}

However, I'm not sure how to overload the comparison operator appropriately in this situation for it to behave like that (since I can think of no logical less than operator that would suit this class). 但是,我不确定在这种情况下如何适当地重载比较运算符以使其表现出这样的行为(因为我认为逻辑上不能比适合此类的运算符少)。 This is the current operator I'm using: 这是我正在使用的当前运算符:

   bool operator<(const index_t& rhv)
   {
     if(vertex_index < rhv && normal_index < rhv && texture_index < rhv)
       return true;
     else return false;
    }

It doesn't work, since the find relies on the function returning "false" reflexively when comparison orders reversed. 这是行不通的,因为当比较顺序相反时,查找依赖于该函数反身返回“ false”的功能。

How can I get around this? 我该如何解决?

This is some more specific, compilable code that reproduces the problem: 这是一些更具体的可编译代码,可重现此问题:

 class index_t
     {
     public:
    int vertex;
    int normal;
    int texture;

    bool operator< (const index_t& rhv) const
    {
        if (vertex < rhv.vertex && normal < rhv.normal && texture < rhv.texture)
            return true;
        else return false;
    }
};



map<index_t, int> indexMap;

int main()
{
    index_t i;
    i.vertex = 0;
    i.normal = 0;
    i.texture = 0;

    index_t i2;
    i2.vertex = 1;
    i2.normal = 0;
    i2.texture = 3;

    index_t i4;
    i4.vertex = 1;
    i4.normal = 0;
    i4.texture = 3;

    index_t i5;
    i5.vertex = 6;
    i5.normal = 0;
    i5.texture = 3;

    index_t i8;
    i8.vertex = 7;
    i8.normal = 5;
    i8.texture = 4;

    indexMap.insert(pair<index_t, int>(i, 0));
    indexMap.insert(pair<index_t, int > (i2, 1));


    if (indexMap.find(i5) != indexMap.end())
        cout << "found" << endl;
    else
        cout << "not found" << endl;

    system("pause");
    return 0;
} 

This results in "found" even though i5 is not a part of the map 即使i5不在地图中,这也会导致“找到”

I also tried this: 我也试过这个:

class index_t
{
public:
    int vertex;
    int normal;
    int texture;
};

class index_comparator
{
public: 
    bool operator()(const index_t& lhv, const index_t& rhv) const
    {
        if (lhv.vertex == rhv.vertex && lhv.normal == rhv.normal && lhv.texture == rhv.texture)
            return true;
        else return false;
    }
};

map<index_t, int, index_comparator> indexMap;

int main()
{
    index_t i;
    i.vertex = 0;
    i.normal = 0;
    i.texture = 0;

    index_t i2;
    i2.vertex = 1;
    i2.normal = 0;
    i2.texture = 3;

    index_t i4;
    i4.vertex = 1;
    i4.normal = 0;
    i4.texture = 3;

    index_t i5;
    i5.vertex = 6;
    i5.normal = 0;
    i5.texture = 3;

    index_t i8;
    i8.vertex = 7;
    i8.normal = 5;
    i8.texture = 4;

    indexMap.insert(pair<index_t, int>(i, 0));
    indexMap.insert(pair<index_t, int > (i2, 1));


    if (indexMap.find(i5) != indexMap.end())
        cout << "found" << endl;
    else
        cout << "not found" << endl;

    system("pause");
    return 0;
}

This also results in "found" 这也导致“发现”

The expected results are that when I call std::map::find on a custom class it compares it other keys in the map and only returns true if an exactly same class (containing the same parameters) exists. 预期的结果是,当我在自定义类上调用std :: map :: find时,它将与映射中的其他键进行比较,并且仅当存在完全相同的类(包含相同的参数)时才返回true。 Otherwise it should return false. 否则应返回false。

You have to define a strict order to use class index_t as key in a std::map . 您必须定义严格的顺序,才能将class index_t作为std::map键使用。

It doesn't need to make sense to you – it just has to provide a unique result of less-than for any pairs of index_t instances (and to grant a < b && b < c => a < c). 它不需要对您有意义-它只需要为所有成对的index_t实例提供小于的唯一结果(并授予<b && b <c => a <c)。

The (in question) exposed attempt doesn't seem to fulfil this but the following example would: (有问题的)公开尝试似乎无法实现这一点,但以下示例可以解决:

bool operator<(const index_t &index1, const index_t &index2)
{
  if (index1.vertex != index2.vertex) return index1.vertex < index2.vertex;
  if (index1.normal != index2.normal) return index1.normal < index2.normal;
  return index1.texture < index2.texture;
}

Your comparison function doesn't have to be logical, it just has to impose a strict weak ordering . 您的比较函数不必具有逻辑性,而仅需施加严格的弱排序 Here's a version that works. 这是一个有效的版本。

bool operator<(const index_t& rhv) const
{
    if (vertex < rhv.vertex)
        return true;
    if (vertex > rhv.vertex)
        return false;
    if (normal < rhv.normal)
        return true;
    if (normal > rhv.normal)
        return false;
    if (texture < rhv.texture)
        return true;
    if (texture > rhv.texture)
        return false;
    return false;
}

Since this is not a reasonable operator< for your class it would be better to rename it, to avoid confusion. 由于对于您的课程而言,这不是一个合理的operator< ,因此最好将其重命名以避免混淆。

struct IndexLT
{
    bool operator()(const index_t& lhs, const index_t& rhs)
    {
        // logic as before
    }
};

Then use this newly declared functor like this 然后像这样使用这个新声明的函子

std::map<index_t, whatever, IndexLT> my_map;

Yet another alternative would be to use a std::unordered_map since ordering doesn't seem to be significant. 另一个选择是使用std::unordered_map因为排序似乎并不重要。

Your ordering doesn't fulfill the requirements, it has to be what is called a "strict weak ordering relation". 您的订购不符合要求,必须是所谓的“严格弱订购关系”。 It's easiest to not implement that yourself, but instead use existing functionality. 不用自己实现,而是使用现有功能是最容易的。 Examle: 考试:

#include <tuple>

bool operator()(const index_t& lhv, const index_t& rhv) const
{
    return std::tie(lhv.vertex, lhv.normal, lhv.texture) <
       std::tie(rhv.vertex, rhv.normal, rhv.texture);
}

The simplest way to implement the operator is with tuples, it does all the hard work for you: 实现操作符的最简单方法是使用元组,它会为您完成所有艰苦的工作:

bool operator<(const index_t& rhv)
{
   return std::tie(vertex_index, normal_index, texture_index) < std::tie(rhv.vertex_index, rhv.normal_index, rhv.texture_index);
}

This is equivalent to the required logic: 这等效于所需的逻辑:

bool operator<(const index_t& rhv)
{
   if (vertex_index != rhv.vertex_index)
   {
      return vertex_index < rhv.vertex_index;
   }
   if (normal_index!= rhv.normal_index)
   {
      return normal_index< rhv.normal_index;
   }
   return texture_index< rhv.texture_index;
}

In c++20 this gets even easier with the spaceship operator which does everything for you: c ++ 20中 ,使用宇宙飞船运算符可以更加轻松地完成该任务:

auto operator<=>(const index_t&) const = default;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM