How does the compare function in std::map in C++ w

2020-06-28 00:49发布

I have a map in my project. Every time I insert a new element, I want to ensure that the key of the new element I insert is at least a minimum width apart from other elements in the map. To do this I wrote a custom compare class like this:

class PulseCompare
{
public:
    PulseCompare(int minwidth_):minwidth(minwidth_){};
    bool operator()(const int x, const int y) const {
        if(abs(x-y)>minwidth) return false;
        else return true;
    }
private:
    int minwidth;
};

and created the map like this:

std::map<int,float,PulseCompare> pulsemap(PulseCompare(256));

and before I insert an element I use the map.find method like this:

if ( pulsemap.find(1600) == pulsemap.end() ) {
  // not found so I can insert
} else {
  // found
}

But the problem is that when map tries to reflexively compare using the above compare function by interchanging the values of x and y, it would get a true for both the cases which is usually not the case with normal comparison operators like < and >

On the cplusplus documentation page on std::map::key_comp here it says and I quote

The comparison object of a map object is set on construction. Its type (member key_compare) is the third template parameter of the map template. By default, this is a less object, which returns the same as operator "<".

This object determines the order of the elements in the container: it is a function pointer or a function object that takes two arguments of the same type as the element keys, and returns true if the first argument is considered to go before the second in the strict weak ordering it defines, and false otherwise.

Two keys are considered equivalent if key_comp returns false reflexively (i.e., no matter the order in which the keys are passed as arguments).

But this does not say about a case where it is reflexively true. Can anyone tell me what will be its behaviour then? Or should I do this interval comparison only by iterating over the entire map?

标签: c++ stl stdmap
3条回答
冷血范
2楼-- · 2020-06-28 01:26

Why not just use this as your comparator?

return minwidth < (y - x);

Here is a working example: http://coliru.stacked-crooked.com/a/e115d3a2f6714773

查看更多
The star\"
3楼-- · 2020-06-28 01:27

Actually if you rewrite your comparator this way, it should work:

bool operator()(const int x, const int y) const {
    if(abs(x-y)<=minwidth) return false;
    else return x < y;
}

And you do not need to use find before inserting, you are doing double work. Just try to insert, if such key exists map will reject that insertion. std::map::insert also returns std::pair<iterator,bool> so you can check if insertion was successful or not.

查看更多
看我几分像从前
4楼-- · 2020-06-28 01:34
  1. The comparator that is used in std::map must provide a strict weak ordering of the objects.
  2. Your comparator does not.
  3. Therefore, your instance of std::map will produce undefined behavior.

Note: It's generally easier to just have the comparator provide a total ordering.

Additionally, let's describe what a strict weak ordering requires. Here, I'm taking excerpts from C++ 2011, Section 25.4.

  • You're going to create a functor Compator comp; I'm going to refer to comp(lhs, rhs) as a function that returns a boolean. It's helpful to think of it as lhs < rhs.
  • We're going to create a function equiv(lhs, rhs). This is defined as (!comp(lhs, rhs) && !comp(rhs, lhs)). So, !(lhs < rhs) && !(rhs < lhs).

We're required to have the following rules hold:

comp(a, b) && comp(b, c) implies comp(a, c)
equiv(a, b) && equiv(b, c) implies equiv(a, c)
查看更多
登录 后发表回答