Custom ordered set give "invalid operator<" of find(...) - c++

Have a issue when I'm trying to find element in a custom ordered set.
File: c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree
Line: 1746
Expression: invalid operator<
I need a set of strings where the elements are ordered accordingly my needs.
Comparator object:
struct OrderComparator {
public:
static map<string,int> valueOrder;
bool operator()( const string lhs, const string rhs ) {
map<string,int>::iterator resultLhs,resultRhs;
resultLhs = valueOrder.find(lhs);
resultRhs = valueOrder.find(rhs);
if (resultLhs == valueOrder.end() || resultRhs == valueOrder.end())
{
return false;
}
else {
bool result = resultLhs->second <= resultRhs->second;
return result;
}
}
static map<string,int> create_map()
{
map<string,int> m;
m["A"] = 1;
m["B"] = 2;
m["C"] = 3;
m["D"] = 4;
return m;
}
};
Comparator is working fine!
But when I'm trying to search in the set getting mentioned error.
typedef set<string, OrderComparator> TREESET_CMP;
...
TREESET_CMP::iterator it = myTree.find(obj); <-fails
...
Will be glad if some one can tell me why this is happening and how to fix it.
Full working Mini Example:
#include "stdafx.h"
#include <string>
#include <set>
#include <map>
#include <iterator>
#include <algorithm>
using namespace std;
#include <stdio.h>
#include <tchar.h>
struct OrderComparator {
public:
static map<string,int> valueOrder;
bool operator()( const string lhs, const string rhs ) {
map<string,int>::iterator resultLhs,resultRhs;
resultLhs = valueOrder.find(lhs);
resultRhs = valueOrder.find(rhs);
if (resultLhs == valueOrder.end() || resultRhs == valueOrder.end())
{
return false;
}
else {
bool result = resultLhs->second <= resultRhs->second;
return result;
}
}
static map<string,int> create_map()
{
map<string,int> m;
m["A"] = 1;
m["B"] = 2;
m["C"] = 3;
m["D"] = 4;
return m;
}
};
map<string,int> OrderComparator::valueOrder = OrderComparator::create_map();
typedef set<string, OrderComparator> TREESET_CMP;
int _tmain(int argc, _TCHAR* argv[])
{
TREESET_CMP myTree;
myTree.insert("B");
myTree.insert("C");
myTree.insert("A");
TREESET_CMP::const_iterator it = myTree.find("A");
system("PAUSE");
}

Your comparison doesn't define a strict weak ordering
A strict weak ordering must have these invariants (quoted from the link above)
Irreflexivity f(x, x) must be false.
Antisymmetry f(x, y) implies !f(y, x)
Transitivity f(x, y) and f(y, z) imply f(x, z).
Transitivity of equivalence Equivalence (as defined above) is transitive: if x is equivalent to y and y is equivalent to z, then x is equivalent to z. (This implies that equivalence does in fact satisfy the mathematical definition of an equivalence relation.)
Yours fails at least Irreflexivity (comparing an object to itself must be false) and Antisymmetry (if x is less-than y, then y is not less-than x)
Basically, <= is not a valid ordering, because x <= x returns true, which means you can never find an element in the set. To find an element the set looks for an element with the property !cmp(key, element) && !cmp(element, key) but that can never work for your ordering.
The simplest fix might be to change <= to <, but there could be other problems.

Your comparator can be this:
bool operator()( const string &lhs, const string &rhs ) {
map<string,int>::iterator resultLhs,resultRhs;
resultLhs = valueOrder.find(lhs);
resultRhs = valueOrder.find(rhs);
if (resultLhs == valueOrder.end()) return false;
if (resultRhs == valueOrder.end()) return true;
return resultLhs->second < resultRhs->second;
}
You can replace 2 lines to:
if (resultRhs == valueOrder.end()) return false;
if (resultLhs == valueOrder.end()) return true;
If you want strings that do not exist in your map to be sorted before that do.

Try declaring your comparison method to be const and to use const references for the arguements. The first fix is to ensure that you can call your method with constant versions of your OrderComarator the second fix is to avoid copying.
bool operator()( const string& lhs, const string& rhs ) const {

Related

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. 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:
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.
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
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. Otherwise it should return false.
You have to define a strict order to use class index_t as key in a 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).
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;
}
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:
auto operator<=>(const index_t&) const = default;
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);
}
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.
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.

search a structure element in map

i am new to C++(also english).
i want to search {1,2,3} in the map and if it exists , print TRUE on screen
but i can not
my code comes below
can you help me?
#include <iostream>
#include <map>
#include <iterator>
#define PKT_UNIT_MAX_LEN 10000
using namespace std;
struct PKT_UNIT
{
int len;
unsigned int checksum;
unsigned char data[PKT_UNIT_MAX_LEN];
};
int main()
{
map<int,PKT_UNIT> maharan;
maharan.insert(pair<int,PKT_UNIT>(1,{1,2,3}));
map<int,PKT_UNIT> ::iterator it;
it=maharan.begin();
for(it=maharan.begin(); it != maharan.end(); it++ )
{
if (maharan.find(it)!=maharan.end())
{
if (it->second.len==1 && it->second.checksum==2 && it->second.data==3)
cout<<"TRUE"<<endl;
}
return 0;
}
map::find takes something comparable to the key_type, not the mapped_type, and certainly not an iterator. Searching for the value is not what std::map is designed to support. You can instead use the generic searching algorithms.
bool operator==(const PKT_UNIT& lhs, const PKT_UNIT& rhs)
{
return (lhs.len == rhs.len)
&& (lhs.checksum == rhs.checksum)
&& std::equal(lhs.data, lhs.data + lhs.len, rhs.data);
}
int main()
{
PKT_UNIT needle{1,2,3};
std::map<int,PKT_UNIT> maharan;
maharan.insert(pair<int,PKT_UNIT>(1,needle));
auto it = std::find_if(maharan.begin(), maharan.end(), [&needle](auto & item){ return item.second == needle; });
if (it != maharan.end())
{
std::cout << "TRUE";
}
return 0;
}
You should overload the equality operator for PKT_UNIT. Then you should just use std::map::find to find what you are looking for.
bool operator==(const PKT_UNIT& lhs, const PKT_UNIT& rhs)
{
return (lhs.len == rhs.len) &&
(lhs.checksum == rhs.checksum) &&
std::equal(lhs.data,lhs.data + PKT_UNIT_MAX_LEN,rhs.data);
}
The you can do something like this:
PKT_UNIT goal {1,2,3};
for (const auto& e : maharan)
{
if (e.second == goal)
{
std::cout << "TRUE\n";
break;
}
}
Also, as it seems you don't need the key, maybe you want to use std::set and use a search algorithm, or std::unordered_set, in which case you wouldn't need a search algorithm at all.

std::map's find fails to find, but element in map with manual scan

I am using std::map and a list to keep track of windowing over elements and associated scores. When a window is full, I want to pop an element off the windows queue and remove it from the map. Because there can be duplicates, the map keeps track of how many times each element in the window was encountered. I'm also using an ordered map so that I can keep getting the minimum values in a given window.
My problem is that find() is returning end() when it is not expected to.
And when I iterate through the map, I find the element to be present. I don't want to sacrifice the logarithmic complexity of using map.
tl;dr: std::map says an element isn't in the map. A manual scan says it is.
[Edit: Bryan Chen's suggestion fixed the map. Thank you!]
#include <cstdint>
#include <cstdio>
#include <cinttypes>
#include <map>
#include <list>
#include <vector>
#include "util.h"
#include "kmerutil.h"
namespace kpg {
struct elscore_t {
uint64_t el_, score_;
INLINE elscore_t(uint64_t el, uint64_t score): el_(el), score_(score) {
LOG_ASSERT(el == el_);
LOG_ASSERT(score == score_);
}
INLINE elscore_t(): el_(0), score_(0) {}
inline bool operator <(const elscore_t &other) const {
return score_ < other.score_ || el_ < other.el_; // Lexicographic is tie-breaker.
}
inline bool operator ==(const elscore_t &other) const {
return score_ == other.score_ && el_ == other.el_; // Lexicographic is tie-breaker.
}
std::string to_string() const {
return std::to_string(el_) + "," + std::to_string(score_);
}
};
struct esq_t: public std::list<elscore_t> {
};
typedef std::map<elscore_t, unsigned> esmap_t;
class qmap_t {
// I could make this more efficient by using pointers instead of
// elscore_t structs.
// *maybe* TODO
// Could also easily templatify this module for other windowing tasks.
esq_t list_;
#if !NDEBUG
public:
esmap_t map_;
private:
#else
esmap_t map_;
#endif
const size_t wsz_; // window size to keep
public:
void add(const elscore_t &el) {
auto it(map_.upper_bound(el));
if(it->first == el) ++it->second;
else map_.emplace(el, 1);
}
void del(const elscore_t &el) {
auto f(map_.find(el));
if(f == map_.end()) {
LOG_DEBUG("map failed :(\n");
for(f = map_.begin(); f != map_.end(); ++f)
if(f->first == el)
break;
}
LOG_ASSERT(f != map_.end());
if(--f->second <= 0)
map_.erase(f);
}
uint64_t next_value(const uint64_t el, const uint64_t score) {
list_.emplace_back(el, score);
LOG_ASSERT(list_.back().el_ == el);
LOG_ASSERT(list_.back().score_ == score);
add(list_.back());
if(list_.size() > wsz_) {
//fprintf(stderr, "list size: %zu. wsz: %zu\n", list_.size(), wsz_);
//map_.del(list_.front());
del(list_.front());
list_.pop_front();
}
LOG_ASSERT(list_.size() <= wsz_);
return list_.size() == wsz_ ? map_.begin()->first.el_: BF;
// Signal a window that is not filled by 0xFFFFFFFFFFFFFFFF
}
qmap_t(size_t wsz): wsz_(wsz) {
}
void reset() {
list_.clear();
map_.clear();
}
};
}
This is not a valid strict weak ordering:
return score_ < other.score_ || el_ < other.el_;
You have elscore_t(0, 1) < elscore_t(1, 0) and elscore_t(1, 0) < elscore_t(0, 1).
As T.C. pointed out in his answer, your operator< is not correct.
You can use std::tie to do lexicographical comparison
return std::tie(score_, el_) < std::tie(other.score_, other.el_);
Otherwise you can do
if (score_ == other.score_) {
return el_ < other.el_; // use el_ to compare only if score_ are same
}
return score_ < other.score_;

Is there a built in function for std::string in C++ to compare two strings alphabetically when either string can be uppercase or lowercase?

I know for C++ that basic comparative operators can accomplish the task if both words are either entirely lower or entirely upper case. I have an array of strings and letters can vary from lower to upper. Here is a small examle of the kind of strings I can be working with:
"ABUNDANT LIFE CH"
"NEW LIFE WMN MNSTRY"
"NEW LIFE ASSEMBLY"
I know in Java there exists the function String.compareToIgnoreCase(). Is there a C++ equivalent of this function?
I don't know of any case-insensitive functions in the standard library, but you can specify a custom predicate for std::equal:
std::string a("hello");
std::string b("HELLO");
std::cout << std::equal(a.begin(), a.end(), b.begin(),
[] (const char& a, const char& b)
{
return (std::tolower(a) == std::tolower(b));
});
For a solution which takes locale into account, refer to Case insensitive std::string.find().
#include <locale>
template<typename charT = std::string::value_type>
struct my_equal {
my_equal( const std::locale& loc ) : loc_(loc) {}
bool operator()(charT ch1, charT ch2) {
return std::toupper(ch1, loc_) == std::toupper(ch2, loc_);
}
private:
const std::locale& loc_;
};
int main()
{
std::string a("hello");
std::string b("HELLO");
std::cout << std::equal(a.begin(), a.end(), b.begin(),
my_equal<>(std::locale()));
}
Yes there is a case insensitive way to compare strings in C++. The key is that std::string is a template:
template <class charT,
class traits = char_traits<charT>,
class Allocator = allocator<charT>>
class basic_string;
The traits here control how the charT's relate to each other. For normal std::string, they do what you'd expect, but we can just write our own traits that are case insensitive:
struct case_insensitive_traits
: char_traits<char>
{
static bool eq(char a, char b) { return tolower(a) == tolower(b); }
static bool ne(char a, char b) { return !eq(a, b); }
static bool lt(char a, char b) { return tolower(a) < tolower(b); }
static bool gt(char a, char b) { return tolower(a) > tolower(b); }
static int compare(const char* a, const char* b, size_t n)
{
for (size_t i = 0; i < n; ++i) {
int delta = tolower(a[i]) - tolower(b[i]);
if (delta != 0) return delta;
}
return 0;
}
static const char* find(const char* s, size_t n, char c)
{
c = tolower(c);
for (size_t i = 0; i < n; ++i, ++s) {
if (tolower(*s) == c) return s;
}
return nullptr;
}
};
With that:
using case_insensitive_string = std::basic_string<char, case_insensitive_traits>;
case_insensitive_string a{"hello"};
case_insensitive_string b{"hElLo"};
assert(a == b);
You can use Boost String Algorithms:
#include <string>
#include <cassert>
#include <boost/algorithm/string.hpp>
int main() {
std::string s { "Test" };
assert(boost::iequals(s, "TEST"));
}
In C++ usually less-than (bool less(type, type)) is used in places of tri-value function compare (int cmp(type, type)). Of course each one of them can be trivially defined in terms of the other.
Here's something that can easily be plugged into STL algorithms:
template<class String>
struct ciless {
locale l_;
explicit ciless(locale l = locale()) : l_(l) {}
bool operator() (
String const &a
, String const &b) const
{
auto fa = a.begin();
auto fb = b.begin();
while (fa != a.end()
&& fb != b.end()
&& (tolower(*fa, l_) == tolower(*fb, l_)))
{
++fa;
++fb;
}
return
(fa == a.end() && fb != b.end())
|| (
fa != a.end()
&& fb != b.end()
&& tolower(*fa, l_) < tolower(*fb, l_));
}
};
And here's something that can convert less() into java-style compare():
template<class T, class Less = std::less<T>>
struct compare
{
Less l_;
explicit compare(Less l = Less()) : l_(l) {}
int operator() (
T const &a
, T const &b) const
{
if (l_(a, b))
return -1;
if (l_(b, a))
return 1;
return 0;
}
};
Nothing standard, but if you happen to be developing for Windows or have access to a Posix interface you could use the following:
https://msdn.microsoft.com/en-us/library/k59z8dwe.aspx
// didn't run it through a compiler
// but it would look like something like this:
{
using namespace std;
string a = "HELLO"s;
string b = "HelLO"s;
bool bIsMatch = _stricmp(a.c_str(), b.c_str()) == 0; // bIsMatch = true
}
There's a much simpler solution that doesn't involve coding loops (ugh!) or Boost:
string a = "Foo";
string b = "foo"
return !_stricmp(a.str(), b.str());
The strings are converted to char[]s, then compared, case-insensitive. Returns true if they're equal.
(My biased opinion: C++ should provide this functionality as a string method. Forcing millions of users to hand-code loops for such a common operation is disgusting, and introduces unnecessary "noise" into the code.)

Find function is not working

I want to insert object of struct one as a unique key in map. So i have written operator() function but find is not working even element exist in map.
#include <iostream>
#include<map>
#include <stdio.h>
#include <string.h>
#include <math.h>
using namespace std;
struct one
{
char* name_;
double accuracy_;
one(char* name, double accuracy)
{
name_ = name;
accuracy_ = accuracy;
}
};
const float Precision = 0.000001;
struct CompLess:public std::binary_function<const one, const one, bool>{
bool operator()(const one p1, const one p2) const
{
if (strcmp(p1.name_, p2.name_)<0)
{
return true;
}
if(((p1.accuracy_) - (p2.accuracy_)) < Precision and
fabs((p1.accuracy_) - (p2.accuracy_))> Precision)
{
return true;
}
return false;
}
};
typedef map<const one,int,CompLess> Map;
int main( )
{
one first("box",30.97);
one first1("war",20.97);
Map a;
a.insert(pair<one,int>(first,1));
a.insert(pair<one,int>(first1,11));
if(a.find(first1) == a.end())
{
cout<<"Not found"<<endl;
}
else
{
cout<<"Found"<<endl;
}
return 0;
}
Your comparison class doesn't induce a strict ordering. You should change it to this:
bool operator()(const one p1, const one p2) const
{
if (strcmp(p1.name_, p2.name_) == 0)
{
if (((p1.accuracy_) - (p2.accuracy_)) < Precision and
fabs((p1.accuracy_) - (p2.accuracy_))> Precision)
{
return true;
}
}
return false;
}
In your version first1 was less than first because strcmp("war", "box") > 0 (first condition is false) and 20.97 < 30.97 (second condition is true), but in the same time first was less than first1, because strcmp("box", "war") < 0 (first condition is true). You should compare the second dimension only if the first one is equal - that's the good rule of thumb for less comparisons.