std::map insert error: no operator "<" matches these operands - c++

Trying to brush up on my C++ and STL proficiency, running into a problem with std::map keyed by a structure I've defined. Relevant code:
typedef struct key_t {
int a;
int b;
bool operator==(const key_t& rhs)
{
return (a == rhs.a) && (b == rhs.b);
}
bool operator<(const key_t& rhs) //added the when I saw this error, didn't help
{
return a < rhs.a;
}
} key_t;
std::map<key_t, int> fooMap;
void func(void)
{
key_t key;
key.a = 1;
key.b = 2;
fooMap.insert(std::pair<key_t, int>(key, 100));
}
Error looks like this:
"/opt/[redacted]/include/functional", line 133: error: no operator "<" matches these operands
operand types are: const key_t < const key_t
detected during:
instantiation of "bool std::less<_Ty>::operator()(const _Ty &, const _Ty &) const [with _Ty=key_t]" at line 547 of "/opt/[redacted]/include/xtree"
instantiation of "std::_Tree<_Traits>::_Pairib std::_Tree<_Traits>::insert(const std::_Tree<_Traits>::value_type &) [with _Traits=std::_Tmap_traits<key_t, UI32, std::less<key_t>, std::allocator<std::pair<const key_t, UI32>>, false>]"
What am I doing wrong? Is it just flat-out awful/impossible to use structures as a map key? Or something else I'm overlooking?

This
bool operator<(const key_t& rhs)
needs to be a const method
bool operator<(const key_t& rhs) const
The two are different signatures, and std::less looks for the latter. The latter, as a const method, implying that it won't modify the object. The former however without const may imply that a modification to this may be performed.
In general its a good idea to have const methods, even if you can cast in away, it implies a promise to the client that no modifications will take place.

For starters, the operators have to be const. (And you don't need the == operator.)
And where did you learn to use a typedef for a struct. There's no reason for it.
And finally, if you want both elements to participate as part of
the key, you'll have to compare both of them:
struct Key
{
int a;
int b;
bool operator<( Key const& rhs ) const
{
return a < rhs.a
|| ( !(rhs.a < a) && b < rhs.b );
}
};
Otherwise, Key( 1, 2 ) and Key( 1, 3 ) will effectively be
equal.

Related

Compare std::set with custom comparer

I am trying to use the std::tie to implement the operator< in order to create a map of structs that contain a set. The same code without templates seem that it works. I am getting this message code from my compiler:
/usr/include/c++/4.8/bits/stl_algobase.h:888: error: no match for operator< (operand types are const SiPa<int, int> and const SiPa<int, int>)
if (*__first1 < *__first2)
^
Everything compiles if I comment the myMap.insert({akey, true}); line.
Any hints?
template<class I = int, class S = int>
struct SiPa
{
I i;
S s;
};
template<class I = int, class S = int>
struct SiPaComparator
{
bool operator() (const SiPa<I, S>& first, const SiPa<I, S>& second) const
{
return std::tie(first.i, first.s) < std::tie(second.i, second.s);
}
};
template<class I = int, class S = int>
struct AKey
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
};
template<class I = int, class S = int>
struct AKeyComparator
{
bool operator() (const AKey<I, S>& first, const AKey<I, S>& second) const
{
return std::tie(first.keySet, first.keyI) < std::tie(second.keySet, second.keyI);
}
};
int main()
{
AKey<int,int> akey;
std::map<AKey<int,int>, bool, AKeyComparator<int,int>> myMap;
myMap.insert({akey, true});
}
You need add operator< for struct SiPa, std::map require it
template<class I = int, class S = int>
struct SiPa {
I i;
S s;
bool operator<(const SiPa<I, S> &ref) {
return i < ref.i && s < ref.s;
}
};
In general, comparators on maps and sets are stateful. When comparing two different sets or maps, there is no obvious way to pick which one to use.
So when comparing different sets and maps via <, you get std::lexographical_compare with no Compare argument, which uses <. (Note this sucks for sets of pointers to objects not from the same array)
struct order_by_tie {
template<class Lhs, class Rhs,
class=std::enable_if_t<
std::is_base_of<order_by_tie, Lhs>::value
&& std::is_base_of<order_by_tie, Rhs>::value
>
>
friend bool operator<(Lhs const& lhs, Rhs const& rhs) {
return as_tie(lhs) < as_tie(rhs);
}
};
order_by_tie is intended to be inherited from. It uses ADL (argument dependent lookup) to enable < on its descendent classes, implemented by calling the free function as_tie on each side then doing a <.
We use it as follows:
template<class I = int, class S = int>
struct SiPa:order_by_tie
{
I i;
S s;
friend auto as_tie( SiPa const& self ) {
return std::tie(self.i, self.s);
}
};
template<class I = int, class S = int>
struct AKey:order_by_tie
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
friend auto as_tie( AKey const& self ) {
return std::tie(self.keySet, self.keyI);
}
};
then
std::map<AKey<int,int>, bool> myMap;
works.
as_tie uses C++14, because the alternative is annoying. You can add a -> decltype(std::tie( blah, blah )) for C++11 (repeating yourself).
According to http://www.cplusplus.com/reference/set/set/operators/
The other operations also use the operators == and < internally to compare the elements, behaving as if the following equivalent operations were performed:
Notice that none of these operations take into consideration the internal comparison object of neither container.
So the comparaison of std::set<SiPa<I, S>, SiPaComparator<I,S>> is done with
operator < (const SiPa<I, S>&, const SiPa<I, S>&)
and not with
SiPaComparator<I, S>{}
The workaround is to define that operator <.

Why "no match for 'operator<'" when I declared it?

It works for the struct xy that I declared. Why doesn't the same pattern work for complex<int>?
#include <complex>
#include <set>
using namespace std;
struct xy {
int x, y;
};
bool operator< (const xy &a, const xy &b) {
return a.x < b.x;
}
bool operator< (const complex<int> &a, const complex<int> &b) {
return a.real() < b.real();
}
int main() {
xy q;
set<xy> s;
s.insert(q);
complex<int> p;
set< complex<int> > t; //If I comment out these two lines,
t.insert(p); //it compiles fine.
return 0;
}
The error message:
In file included from c:\m\lib\gcc\mingw32\4.8.1\include\c++\string:48:0,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\locale_classes.h:40,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\ios_base.h:41,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\ios:42,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\istream:38,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\sstream:38,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\complex:45,
from test.cpp:1:
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h: In instantiation of 'bool less<>::operator()(const _Tp&, const _Tp&) const':
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1321:11: required from 'pair<> _Rb_tree<>::_M_get_insert_unique_pos(const key_type&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1374:47: required from 'pair<> _Rb_tree<>::_M_insert_unique(_Arg&&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_set.h:463:29: required from 'pair<> __cxx1998::set<>::insert(const value_type&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\debug\set.h:220:59: required from 'pair<> __debug::set<>::insert(const value_type&)'
test.cpp:28:19: required from here
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h:235:20:
error: no match for 'operator<' (operand types are 'const std::complex<int>' and 'const std::complex<int>')
{ return __x < __y; }
My best guess is that this has something to do with complex<T> being a class, not a struct. By I can't see the logic of why that should make a difference. Or is it some template horribleness?
What I see happening is that the STL at some point tries (roughly speaking) to do a < b, where a and b are complex<int> instances. So it's looking for bool operator< (const complex<int> &a, const complex<int> &b). Well, there is exactly that declared just above main(). Why is it being unreasonable? I thought maybe it didn't like them being references. But removing the ampersands made no difference to its complaint.
One option is to write a custom comparison functor, and instantiate the set with this.
#include <complex>
#include <set>
bool operator< (const std::complex<int> &a, const std::complex<int> &b) {
return a.real() < b.real();
}
struct my_less {
bool operator() (const std::complex<int>& lhs, const std::complex<int>& rhs) const{
return lhs < rhs;
}
};
int main() {
std::complex<int> p;
std::set< std::complex<int>, my_less > t;
t.insert(p);
return 0;
}
Sample on Coliru
It works for the struct xy that I declared. Why doesn't the same pattern work for complex<int>?
The reason is that when you use types from namespace std only, like std::set and std::complex, the compiler has no reason to look for operators in any other namespaces.
With struct xy this is different, as the operator is declared together with the type.
And also, the current standard says:
The effect of instantiating the template complex for any type other than float, double, or long double is
unspecified.
So using complex<int> might, or might not, work depending on which compiler you use. "Unspecified" is not as bad as "undefined", but
of course not very reliable or portable.
As of today, I am facing this issue with my Ubuntu's g++. Suppose I have:
namespace X { namespace Y { class C { ... }; } }
Now operator== is recognized if it's defined in a global namespace, such as:
bool operator== (const X::Y::C& lhs, const X::Y::C& rhs) { return lhs == rhs; }
However, somehow compiler doesn't recognize operator<, if defined in the same way.
Now following way works well:
namespace X { namespace Y {
bool operator< (const C& lhs, const C& rhs) { return lhs < rhs; } } }
However, whether you should do it the same with expanding standard namespace std is a controversial topic. :-)

Error in operator function in multimap

I am trying to create a multimap using multikey structure as a key and I am getting a error described below:
code:
struct stCont
{
long long Tok;
char Reserved;
long long Asset;
}
struct MultiKey {
char InstrumentName[6];
char Symbol[10];
long long ExpiryDate;
}
struct myComp
{
bool operator() (const MultiKey& lhs, const MultiKey& rhs)
{
if((lhs.ExpiryDate==rhs.ExpiryDate)&&(memcmp(lhs.InstrumentName,rhs.InstrumentName,6))&&(memcmp(lhs.Symbol,rhs.Symbol,10)))
{
return 1;
}
return 0;
}
};
std::multimap<MultiKey, stCont,myComp> cont_map;
error:
expression having type 'const myComp' would lose some const-volatile qualifiers in order to call 'bool myComp::operator ()(const MultiKey &,const MultiKey &)'
you should rewrite the multimap code like this and remove the mycomp structure:
struct MultiKey {
char InstrumentName[6];
char Symbol[10];
long long ExpiryDate;
bool operator< (const MultiKey& lhs)const
{
if((lhs.ExpiryDate==ExpiryDate)&&(memcmp(lhs.InstrumentName,InstrumentName,6))&&(memcmp(lhs.Symbol,Symbol,10)))
{
return true;
}
return false;
}
};
Why don't you just write just write operator < for MultiKey? Or you'll have to change myComp because it isn't what multimap wants anyway (it wants a less-than comparison).
Look at the The C++11 Standard, §23.4.5.1 and the header:
template <class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T> > >
class multimap {
public:
// ...
class value_compare {
friend class multimap;
protected:
Compare comp;
value_compare(Compare c) : comp(c) { }
public:
typedef bool result_type;
typedef value_type first_argument_type;
typedef value_type second_argument_type;
bool operator()(const value_type& x, const value_type& y) const {
return comp(x.first, y.first);
}
};
// ...
};
The comparison function as defined by class value_compare is const. Now, I may be misinterpreting the standard, but this definition seems to be invalid if the operator() is non-const in class Compare.
As to why does it work for some people... Perhaps some finer points about instantiation rules prevent this from being an error, or the implementations are not requierd to adhere strictly to the type definitions in the standard; if so, I'd be glad if someone more versed with The Standard could clarify.
To fix the compile error, declare the comparison function to be a const member:
bool operator() (const MultiKey& lhs, const MultiKey& rhs) const
^^^^^
Then you have another problem: the comparator needs to perform a "less than" comparison, but yours does an equality comparison. You want something like
if (lhs.ExpiryDate < rhs.ExpiryDate) return true;
if (lhs.ExpiryDate > rhs.ExpiryDate) return false;
if (memcmp(lhs.InstrumentName,rhs.InstrumentName,6) < 0) return true;
if (memcmp(lhs.InstrumentName,rhs.InstrumentName,6) > 0) return false;
if (memcmp(lhs.SymbolName,rhs.SymbolName,10) < 0) return true;
return false;
You may find it more convenient to overload operator< rather than defining a named comparator type, so that you can use std::multimap<MultiKey, stCont> with the default comparator.

How to write a comparison operator in c++?

I'm having an array of structure containing three fields:
struct data{
int s;
int f;
int w;
};
struct data a[n];
In order to sort the array of structure based on field f I'm using my own comparison operator :
bool myf( struct data d1,const struct data d2){
return d1.f < d2.f ;
}
The above operator works fine in inbuilt sort() function :
sort(a,a+n,myf);
but it's not working for upper_bound() function :
upper_bound(a,a+n,someValue,myf);
Can anyone tell me where am I doing wrong ? Is my comparison operator wrong ? If it's wrong, why is it working for sort() function and not upper_bound() ?
I'm getting following on compilation :
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_algo.h: In function ‘_FIter std::upper_bound(_FIter, _FIter, const _Tp&, _Compare) [with _FIter = data*, _Tp = int, _Compare = bool (*)(data, data)]’:
prog.cpp:37: instantiated from here
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_algo.h:2243: error: conversion from ‘const int’ to non-scalar type ‘data’ requested
All you actually need here is to create operator< for your type:
inline bool operator<( const data& lhs, const data& rhs ) {
return lhs.f < rhs.f;
}
and standard algorithms will magically work for you.
In C++ you don't need struct when referring to a type like in C, and you want to pass by const reference to avoid copying.
Edit 0:
The above overloads standard comparison operator < for your type. You would use it implicitly as:
data values[N];
// ... populate
std::sort( values, values + N );
or explicitly with a standard functor:
std::sort( values, values + N, std::less<data>());
Edit 1:
See that compiler tells you _Tp = int in the warning? You need to pass an instance of data as third argument to upper_bound, not int:
data xxx = { 0, 1, 7 };
auto iter = std::upper_bound( values, values + N, xxx );
You can also create overloads for comparing to integers, like:
inline bool operator<( const data& lhs, int rhs ) {
return lhs.f < rhs;
}
inline bool operator<( int lhs, const data& rhs ) {
return lhs < rhs.f;
}
for your original invocation to work.
Primarily, it isn't working because the upper_bound overload that accepts a custom sorting takes four parameters:
// http://en.cppreference.com/w/cpp/algorithm/upper_bound
template< class ForwardIt, class T, class Compare >
ForwardIt upper_bound( ForwardIt first, ForwardIt last, const T& value,
Compare comp );
It was suggested in another answer that you introduce operator< for your type. However, do not do this just for the sake of one specific sorting. Only introduce comparison operators iff they actually make sense for your type.
If you don't follow this rule, future programmers might use your type and wonder about why something works that shouldn't, or vice versa. Your future evil twin may also want to use another sorting for his purpose.
E.g., it makes sense for a complex-datatype class, a SIMD-class (like std::valarray), but it doesn't make any specific sense for example for an Employee class:
Employee foo, bar;
if (bar > foo) {
// is bar taller than foo?
// is bar older than foo?
// is bar working better than foo?
// is bar bigger newbie than foo?
}
Instead, you could do it like this:
namespace employee_ordering {
struct by_name_ascending {
bool operator() (Employee const &lhs, Employee const &rhs) const {
return lhs.name() < rhs.name();
}
};
struct by_name_descending {
bool operator() (Employee const &lhs, Employee const &rhs) const {
return lhs.name() > rhs.name();
}
}
};
....
upper_bound(first, last, ..., employee_ordering::by_name_ascending());

How to use a lambda expression as a template parameter?

How to use lambda expression as a template parameter? E.g. as a comparison class initializing a std::set.
The following solution should work, as lambda expression merely creates an anonymous struct, which should be appropriate as a template parameter. However, a lot of errors are spawned.
Code example:
struct A {int x; int y;};
std::set <A, [](const A lhs, const A &rhs) ->bool {
return lhs.x < rhs.x;
} > SetOfA;
Error output (I am using g++ 4.5.1 compiler and --std=c++0x compilation flag):
error: ‘lhs’ cannot appear in a constant-expression
error: ‘.’ cannot appear in a constant-expression
error: ‘rhs’ cannot appear in a constant-expression
error: ‘.’ cannot appear in a constant-expression
At global scope:
error: template argument 2 is invalid
Is that the expected behavior or a bug in GCC?
EDIT
As someone pointed out, I'm using lambda expressions incorrectly as they return an instance of the anonymous struct they are referring to.
However, fixing that error does not solve the problem. I get lambda-expression in unevaluated context error for the following code:
struct A {int x; int y;};
typedef decltype ([](const A lhs, const A &rhs) ->bool {
return lhs.x < rhs.x;
}) Comp;
std::set <A, Comp > SetOfA;
The 2nd template parameter of std::set expects a type, not an expression, so it is just you are using it wrongly.
You could create the set like this:
auto comp = [](const A& lhs, const A& rhs) -> bool { return lhs.x < rhs.x; };
auto SetOfA = std::set <A, decltype(comp)> (comp);
For comparators used this way, you're still better off with a non-0x approach:
struct A { int x; int y; };
struct cmp_by_x {
bool operator()(A const &a, A const &b) {
return a.x < b.x;
}
};
std::set<A, cmp_by_x> set_of_a;
However, in 0x you can make cmp_by_x a local type (i.e. define it inside a function) when that is more convenient, which is forbidden by current C++.
Also, your comparison treats A(x=1, y=1) and A(x=1, y=2) as equivalent. If that's not desired, you need to include the other values that contribute to uniqueness:
struct cmp_by_x {
bool operator()(A const &a, A const &b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
};
Not sure if this is what you're asking, but the signature of a lambda which returns RetType and accepts InType will be:
std::function<RetType(InType)>
(Make sure to #include <functional>)
You can shorten that by using a typedef, but I'm not sure you can use decltype to avoid figuring out the actual type (since lambdas apparently can't be used in that context.)
So your typedef should be:
typedef std::function<bool(const A &lhs, const A &rhs)> Comp
or
using Comp = std::function<bool(const A &lhs, const A &rhs)>;
the problem is the last template parameter is type not an object, so you might want to do the following
std::set <A, std::fuction<bool(const A &,const A &)>>
SetOfA([](const A lhs, const A &rhs) ->bool {
return lhs.x < rhs.x;
} > SetOfA;
to make it simpler you can do the following:
auto func = SetOfA([](const A lhs, const A &rhs) ->bool { return lhs.x < rhs.x;}
set <A,decltype(func)> SetOfA(func);
cheers