Related
I defined a set with data type as user defined object here.
#include<bits/stdc++.h>
using namespace std;
class triplets{
public:
int x,y,z;
triplets(){
}
triplets(int x,int y,int z){
this->x=x;
this->y=y;
this->z=z;
}
};
class Cmp{
public:
Cmp(){};
bool operator() (const triplets &a, const triplets &b){
if( a.x == b.x){
return a.y < b.y;
}else{
return a.x < b.x;
}
}
};
int main(){
set<triplets,Cmp>s;
s.insert(triplets(2,4,5));
return 0;
}
The code compiles fine in c++11 and c++14 versions. But it doesn't compile in c++17 and above. It throws following error.
In file included from /usr/include/c++/11/map:60,
from /usr/include/x86_64-linux-gnu/c++/11/bits/stdc++.h:81,
from Arrays/TwoPointer/test.cpp:1:
/usr/include/c++/11/bits/stl_tree.h: In instantiation of ‘static const _Key& std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_S_key(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type) [with _Key = triplets; _Val = triplets; _KeyOfValue = std::_Identity<triplets>; _Compare = Cmp; _Alloc = std::allocator<triplets>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type = const std::_Rb_tree_node<triplets>*]’:
/usr/include/c++/11/bits/stl_tree.h:2071:47: required from ‘std::pair<std::_Rb_tree_node_base*, std::_Rb_tree_node_base*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_get_insert_unique_pos(const key_type&) [with _Key = triplets; _Val = triplets; _KeyOfValue = std::_Identity<triplets>; _Compare = Cmp; _Alloc = std::allocator<triplets>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::key_type = triplets]’
/usr/include/c++/11/bits/stl_tree.h:2124:4: required from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_Arg&&) [with _Arg = triplets; _Key = triplets; _Val = triplets; _KeyOfValue = std::_Identity<triplets>; _Compare = Cmp; _Alloc = std::allocator<triplets>]’
/usr/include/c++/11/bits/stl_set.h:521:25: required from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Tp>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(std::set<_Key, _Compare, _Alloc>::value_type&&) [with _Key = triplets; _Compare = Cmp; _Alloc = std::allocator<triplets>; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Tp>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree<triplets, triplets, std::_Identity<triplets>, Cmp, std::allocator<triplets> >::const_iterator; typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other = std::allocator<triplets>; typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key> = __gnu_cxx::__alloc_traits<std::allocator<triplets>, triplets>::rebind<triplets>; typename _Alloc::value_type = triplets; std::set<_Key, _Compare, _Alloc>::value_type = triplets]’
Arrays/TwoPointer/test.cpp:29:13: required from here
/usr/include/c++/11/bits/stl_tree.h:770:15: error: static assertion failed: comparison object must be invocable as const
770 | is_invocable_v<const _Compare&, const _Key&, const _Key&>,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/11/bits/stl_tree.h:770:15: note: ‘std::is_invocable_v<const Cmp&, const triplets&, const triplets&>’ evaluates to false
Any idea how should I define the set?
bool operator() (const triplets &a, const triplets &b){
should be
bool operator() (const triplets &a, const triplets &b) const{
I'm getting a compilation error,
required from 'std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_Arg&&) [with _Arg = Solution::EnhancedNode* const&; _Key = Solution::EnhancedNode*; _Val = Solution::EnhancedNode*; _KeyOfValue = std::_Identity<Solution::EnhancedNode*>; _Compare = Solution::EnhancedNodeComparator; _Alloc = std::allocator<Solution::EnhancedNode*>]'
Unfortunately there is no line number, but my removing sections of code, I believe it has something to do with an std::set that I want to apply a custom comparator to:
class EnhancedNode {
public:
EnhancedNode(TreeNode* node) {
node_ = node;
distance_ = numeric_limits<double>::max();
discarded_ = false;
}
TreeNode* node_;
double distance_;
bool discarded_;
};
struct EnhancedNodeComparator {
bool operator() (const EnhancedNode*& a, const EnhancedNode*& b) const {
return a->distance_ < b->distance_;
}
};
set<EnhancedNode*, EnhancedNodeComparator> closest_;
set<EnhancedNode*, EnhancedNodeComparator> next_;
Am I doing anything obviously wrong?
The comparator of std::set should definitely accept references to the const qualified type which is in the set.
What you got wrong is the qualification of the type in the set. It's EnhancedNode*, so qualifying it with const should give EnhancedNode* const. And not const EnhancedNode*.
This is once again a case of leading const being misleading. Change your original comparator, with reference and all, to this:
struct EnhancedNodeComparator {
bool operator() (EnhancedNode const * const& a, EnhancedNode const * const& b) const {
return a->distance_ < b->distance_;
}
};
You can keep the const-qualified pointee, as well. Since C++ explicitly allows it.
The reason that your issue was fixed when you got rid of the reference, is that top-level const is ignored when passing by value. So it didn't matter the the pointer your function received as an argument wasn't const qualified. It was just a copy of the original.
While creating a Minimal, Complete, VerifiableTM example, I was able to solve my problem. I'm posting it anyway because Google surprisingly had zero hits for the first portions of my error in quotes.
Turns out I didn't need the &s in
struct EnhancedNodeComparator {
bool operator() (const EnhancedNode*& a, const EnhancedNode*& b) const {
I found this out by compiling on a different compiler, http://cpp.sh,
#include <limits>
#include <set>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class EnhancedNode {
public:
EnhancedNode(TreeNode* node) {
node_ = node;
distance_ = numeric_limits<double>::max();
discarded_ = false;
}
TreeNode* node_;
double distance_;
bool discarded_;
};
struct EnhancedNodeComparator {
bool operator() (const EnhancedNode*& a, const EnhancedNode*& b) const {
return a->distance_ < b->distance_;
}
};
int main()
{
set<EnhancedNode*, EnhancedNodeComparator> closest_;
closest_.insert(new EnhancedNode(new TreeNode(1)));
return 0;
}
and getting a more helpful error:
In file included from /usr/include/c++/4.9/set:60:0,
from 3:
/usr/include/c++/4.9/bits/stl_tree.h: In instantiation of 'std::pair<std::_Rb_tree_node_base*, std::_Rb_tree_node_base*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_get_insert_unique_pos(const key_type&) [with _Key = EnhancedNode*; _Val = EnhancedNode*; _KeyOfValue = std::_Identity<EnhancedNode*>; _Compare = EnhancedNodeComparator; _Alloc = std::allocator<EnhancedNode*>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::key_type = EnhancedNode*]':
/usr/include/c++/4.9/bits/stl_tree.h:1498:47: required from 'std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_Arg&&) [with _Arg = EnhancedNode*; _Key = EnhancedNode*; _Val = EnhancedNode*; _KeyOfValue = std::_Identity<EnhancedNode*>; _Compare = EnhancedNodeComparator; _Alloc = std::allocator<EnhancedNode*>]'
/usr/include/c++/4.9/bits/stl_set.h:511:40: required from 'std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(std::set<_Key, _Compare, _Alloc>::value_type&&) [with _Key = EnhancedNode*; _Compare = EnhancedNodeComparator; _Alloc = std::allocator<EnhancedNode*>; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<EnhancedNode*>; std::set<_Key, _Compare, _Alloc>::value_type = EnhancedNode*]'
33:54: required from here
/usr/include/c++/4.9/bits/stl_tree.h:1445:11: error: no match for call to '(EnhancedNodeComparator) (EnhancedNode* const&, EnhancedNode* const&)'
__comp = _M_impl._M_key_compare(__k, _S_key(__x));
^
24:8: note: candidate is:
25:10: note: bool EnhancedNodeComparator::operator()(const EnhancedNode*&, const EnhancedNode*&) const
25:10: note: no known conversion for argument 1 from 'const key_type {aka EnhancedNode* const}' to 'const EnhancedNode*&'
In file included from /usr/include/c++/4.9/set:60:0,
from 3:
/usr/include/c++/4.9/bits/stl_tree.h:1456:7: error: no match for call to '(EnhancedNodeComparator) (EnhancedNode* const&, EnhancedNode* const&)'
if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
^
24:8: note: candidate is:
25:10: note: bool EnhancedNodeComparator::operator()(const EnhancedNode*&, const EnhancedNode*&) const
25:10: note: no known conversion for argument 1 from 'EnhancedNode* const' to 'const EnhancedNode*&'
In file included from /usr/include/c++/4.9/set:60:0,
from 3:
/usr/include/c++/4.9/bits/stl_tree.h: In instantiation of 'std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, _Arg&&) [with _Arg = EnhancedNode*; _Key = EnhancedNode*; _Val = EnhancedNode*; _KeyOfValue = std::_Identity<EnhancedNode*>; _Compare = EnhancedNodeComparator; _Alloc = std::allocator<EnhancedNode*>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<EnhancedNode*>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr = std::_Rb_tree_node_base*]':
/usr/include/c++/4.9/bits/stl_tree.h:1502:38: required from 'std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_Arg&&) [with _Arg = EnhancedNode*; _Key = EnhancedNode*; _Val = EnhancedNode*; _KeyOfValue = std::_Identity<EnhancedNode*>; _Compare = EnhancedNodeComparator; _Alloc = std::allocator<EnhancedNode*>]'
/usr/include/c++/4.9/bits/stl_set.h:511:40: required from 'std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(std::set<_Key, _Compare, _Alloc>::value_type&&) [with _Key = EnhancedNode*; _Compare = EnhancedNodeComparator; _Alloc = std::allocator<EnhancedNode*>; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<EnhancedNode*>; std::set<_Key, _Compare, _Alloc>::value_type = EnhancedNode*]'
33:54: required from here
/usr/include/c++/4.9/bits/stl_tree.h:1140:8: error: no match for call to '(EnhancedNodeComparator) (EnhancedNode*&, EnhancedNode* const&)'
|| _M_impl._M_key_compare(_KeyOfValue()(__v),
^
24:8: note: candidate is:
25:10: note: bool EnhancedNodeComparator::operator()(const EnhancedNode*&, const EnhancedNode*&) const
25:10: note: no known conversion for argument 1 from 'EnhancedNode*' to 'const EnhancedNode*&'
I don't really understand why this is so. All the examples for custom comparators for std::set I found online seemed to add &. If someone could explain as an answer, I would select it as the complete answer.
In the following code, the g++ compiler surprisingly cannot decide which operator to use when they are embedded in a struct to serve as a comparator argument in a set:
#include <string>
#include <set>
struct KeyWord {
std::string str;
int qt;
KeyWord(const std::string aKw = "", const int aQt = 0) : str(aKw), qt(aQt) {}
};
struct CompareKeywords {
bool operator() (const std::string& left, const std::string& right) const {
if (left.size() > right.size()) return true;
else if (left.size() < right.size()) return false;
else return (left < right);
}
bool operator() (const KeyWord& left, const KeyWord& right) {
if (left.str.size() > right.str.size()) return true;
else if (left.str.size() < right.str.size()) return false;
else return (left.str < right.str);
}
};
int main() {
std::set<std::string, CompareKeywords> a;
std::set<KeyWord, CompareKeywords> b;
std::string s("_s_");
KeyWord k("_k_", 1);
a.insert(s);
b.insert(k);
}
Here is the compiler output:
g++ oa.cpp
/usr/include/c++/4.9/bits/stl_tree.h: In instantiation of ‘std::pair<std::_Rb_tree_node_base*, std::_Rb_tree_node_base*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_get_insert_unique_pos(const key_type&) [with _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = CompareKeywords; _Alloc = std::allocator<std::basic_string<char> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::key_type = std::basic_string<char>]’:
/usr/include/c++/4.9/bits/stl_tree.h:1498:47: required from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = CompareKeywords; _Alloc = std::allocator<std::basic_string<char> >]’
/usr/include/c++/4.9/bits/stl_set.h:502:29: required from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(const value_type&) [with _Key = std::basic_string<char>; _Compare = CompareKeywords; _Alloc = std::allocator<std::basic_string<char> >; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<std::basic_string<char> >; std::set<_Key, _Compare, _Alloc>::value_type = std::basic_string<char>]’
oa.cpp:28:13: required from here
oa.cpp:11:8: note: candidate 1: bool CompareKeywords::operator()(const string&, const string&) const
bool operator() (const std::string& left, const std::string& right) const {
^
oa.cpp:16:8: note: candidate 2: bool CompareKeywords::operator()(const KeyWord&, const KeyWord&)
bool operator() (const KeyWord& left, const KeyWord& right) {
^
oa.cpp:11:8: note: candidate 1: bool CompareKeywords::operator()(const string&, const string&) const
bool operator() (const std::string& left, const std::string& right) const {
^
oa.cpp:16:8: note: candidate 2: bool CompareKeywords::operator()(const KeyWord&, const KeyWord&)
bool operator() (const KeyWord& left, const KeyWord& right) {
^
/usr/include/c++/4.9/bits/stl_tree.h: In instantiation of ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, const _Val&) [with _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = CompareKeywords; _Alloc = std::allocator<std::basic_string<char> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::basic_string<char> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr = std::_Rb_tree_node_base*]’:
/usr/include/c++/4.9/bits/stl_tree.h:1502:38: required from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = CompareKeywords; _Alloc = std::allocator<std::basic_string<char> >]’
/usr/include/c++/4.9/bits/stl_set.h:502:29: required from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(const value_type&) [with _Key = std::basic_string<char>; _Compare = CompareKeywords; _Alloc = std::allocator<std::basic_string<char> >; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<std::basic_string<char> >; std::set<_Key, _Compare, _Alloc>::value_type = std::basic_string<char>]’
oa.cpp:28:13: required from here
oa.cpp:11:8: note: candidate 1: bool CompareKeywords::operator()(const string&, const string&) const
bool operator() (const std::string& left, const std::string& right) const {
^
oa.cpp:16:8: note: candidate 2: bool CompareKeywords::operator()(const KeyWord&, const KeyWord&)
bool operator() (const KeyWord& left, const KeyWord& right) {
^
The last lines show the ambiguity where the compiler shows two candidates.
Why this ambiguity exist? How should I supress it?
It looks like some builds of gcc have this peculiar feature of printing these messages out of the blue. For example all builds on coliru do this.
These messages are not errors because the object file is produced, and they are not warnings because -Werror doesn't turn them into errors. They look rather like a compiler bug. Obviously one cannot suppress these non-warnings with compiler flags.
Same exact versions of gcc on my machine don't print any messages with this code. They do print regular (tagged with the coloured "warning", non-suppressible, but turnable-to-error) warnings with similar code.
On coliru, making the second operator() const suppresses the messages.
Two separate struct with only one operator in each of them dedicated to a type solves the problem:
#include <string>
#include <set>
struct KeyWord {
std::string str;
int qt;
KeyWord(const std::string aKw = "", const int aQt = 0) : str(aKw), qt(aQt) {}
};
struct CompareStrings {
bool operator() (const std::string& left, const std::string& right) const {
if (left.size() > right.size()) return true;
else if (left.size() < right.size()) return false;
else return (left < right);
}
};
struct CompareKeywords {
bool operator() (const KeyWord& left, const KeyWord& right) {
if (left.str.size() > right.str.size()) return true;
else if (left.str.size() < right.str.size()) return false;
else return (left.str < right.str);
}
};
int main() {
std::set<std::string, CompareStrings> a;
std::set<KeyWord, CompareKeywords> b;
std::string s("_s_");
KeyWord k("_k_", 1);
a.insert(s);
b.insert(k);
}
There was an error in the initial code:
bool operator() (const std::string& left, const std::string& right) const {
bool operator() (const KeyWord& left, const KeyWord& right) {
Suppressing the const at the end of the first declaration, or adding one to the second one solves the problem. But still, I don't understand why the compiler was confused.
So, either:
bool operator() (const std::string& left, const std::string& right) {
bool operator() (const KeyWord& left, const KeyWord& right) {
or:
bool operator() (const std::string& left, const std::string& right) const {
bool operator() (const KeyWord& left, const KeyWord& right) const {
works.
Note: Wheither a const function or not is discussed here.
Since I want overloading, both functions are expected to have the same behaviour, so const to both or none. If I would have liked different behaviours with one with const and the other without (in the case I would have had some struct members I would have wanted to modify), the second solution below with separate struct for each operator definition would be the solution.
I have the following code, but I get an error on on the last line:
struct coord {
int x, y;
bool operator=(const coord &o) {
return x == o.x && y == o.y;
}
bool operator<(const coord &o) {
return x < o.x || (x == o.x && y < o.y);
}
};
map<coord, int> m;
pair<coord, int> p((coord{0,0}),123);
m.insert(p); // ERROR here
How can I use a struct as key in a map?
I tried to change the code to this:
struct coord {
int x, y;
bool const operator==(const coord &o) {
return x == o.x && y == o.y;
}
bool const operator<(const coord &o) {
return x < o.x || (x == o.x && y < o.y);
}
};
But I'm still getting the following error:
C:\Users\tomc\Desktop\g>mingw32-make g++ test.cpp -std=c++0x In file included from c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/string:5 0:0,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/bits/loc ale_classes.h:42,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/bits/ios
_base.h:43,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/ios:43,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/ostream: 40,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/iostream :40,
from test.cpp:1: c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/bits/stl_function.h: In member function 'bool std::less<_Tp>::operator()(const _Tp&, const
_Tp&) const [with _ Tp = coord]': c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/bits/stl_tree.h:1184:4: inst antiated from 'std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare,
_Alloc>::_M_insert_unique(const _Val&) [with _Key
= coord, _Val = std::pair<const coord, int>, _KeyOfValue = std::_Select1st<std:: pair<const coord, int> >, _Compare = std::less<coord>, _Alloc = std::allocator<std::pair<const coord, int>>]' c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/bits/stl_map.h:501:41: insta ntiated from 'std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key,
_Tp> >, _Compare, typename _Alloc::rebind <std::map<_Key, _Tp,
_Compare, _Alloc>::value_type>::other>::iterator, bool> std ::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::map<_Key, _Tp,
_Compare, _ Alloc>::value_type&) [with _Key = coord, _Tp = int,
_Compare = std::less<coord>, _Alloc = std::allocator<std::pair<const coord, int> >, typename std::_Rb_tree<_ Key, std::pair<const _Key,
_Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _ Compare, typename _Alloc::rebind<std::map<_Key, _Tp, _Compare,
_Alloc>::value_ty pe>::other>::iterator = std::_Rb_tree_iterator<std::pair<const coord, int> >, st d::map<_Key,
_Tp, _Compare, _Alloc>::value_type = std::pair<const coord, int>]' test.cpp:56:12: instantiated from here c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/bits/stl_function.h:230:22: er ror: passing 'const coord' as 'this' argument of 'const bool coord::operator<(co nst coord&)' discards qualifiers mingw32-make: *** [game] Error 1
Try and make operator < const:
bool operator<(const coord &o) const {
(Your = operator should probably be == operator and const as well)
By far the simplest is to define a global "less than" operator for your struct in stead of as a member function.
std::map uses - by default - the 'lessthan' functor which, in turn, uses the global "operator<" defined for the key type of the map.
bool operator<(const coord& l, const coord& r) {
return (l.x<r.x || (l.x==r.x && l.y<r.y));
}
As mentioned in the answer by Andrii, you can provide a custom comparison object to the map instead of defining operator< for your struct. Since C++11, you can also use a lambda expression instead of defining a comparison object. Moreover, you don't need to define operator== for your struct to make the map work. As a result, you can keep your struct as short as this:
struct coord {
int x, y;
};
And the rest of your code could be written as follows:
auto comp = [](const coord& c1, const coord& c2){
return c1.x < c2.x || (c1.x == c2.x && c1.y < c2.y);
};
std::map<coord, int, decltype(comp)> m(comp);
Code on Ideone
Another solution, which may be used for third-party data types, is to pass a Comparison object as third template parameter.
example
I have a small program I want to execute to test something
#include <map>
#include <iostream>
using namespace std;
struct _pos{
float xi;
float xf;
bool operator<(_pos& other){
return this->xi < other.xi;
}
};
struct _val{
float f;
};
int main()
{
map<_pos,_val> m;
struct _pos k1 = {0,10};
struct _pos k2 = {10,15};
struct _val v1 = {5.5};
struct _val v2 = {12.3};
m.insert(std::pair<_pos,_val>(k1,v1));
m.insert(std::pair<_pos,_val>(k2,v2));
return 0;
}
The problem is that when I try to compile it, I get the following error
$ g++ m2.cpp -o mtest
In file included from /usr/include/c++/4.4/bits/stl_tree.h:64,
from /usr/include/c++/4.4/map:60,
from m2.cpp:1:
/usr/include/c++/4.4/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = _pos]’:
/usr/include/c++/4.4/bits/stl_tree.h:1170: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = _pos, _Val = std::pair<const _pos, _val>, _KeyOfValue = std::_Select1st<std::pair<const _pos, _val> >, _Compare = std::less<_pos>, _Alloc = std::allocator<std::pair<const _pos, _val> >]’
/usr/include/c++/4.4/bits/stl_map.h:500: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = _pos, _Tp = _val, _Compare = std::less<_pos>, _Alloc = std::allocator<std::pair<const _pos, _val> >]’
m2.cpp:30: instantiated from here
/usr/include/c++/4.4/bits/stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
m2.cpp:9: note: candidates are: bool _pos::operator<(_pos&)
$
I thought that declaring the operator< on the key would solve the problem, but its still there.
What could be wrong?
Thanks in advance.
The problem is this:
bool operator<(_pos& other)
Should be this:
bool operator<(const _pos& other) const {
// ^^^^ ^^^^^
Without the first const, the right-hand side of the comparison (b in a < b) cannot be const, since without const the function may modify its argument.
Without the second const, the left-hand side of the comparison (a in a < b) cannot be const, since without const the function may modify this.
Internally, the key's of a map are always const.
It should be noted that you should prefer to use nonmember functions. That is, better is a free-function:
bool operator<(const _pos& lhs, const _pos& rhs)
{
return lhs.xi < rhs.xi;
}
In the same namespace as your class. (For our example, just underneath it.)
By the way, in C++ there is no need to prefix the declaration of a struct type variable with struct. This is perfect, and preferred:
_pos k1 = {0,10};
_pos k2 = {10,15};
_val v1 = {5.5};
_val v2 = {12.3};
(Though your type names are admittedly named in an unorthodox manner. :P)
Lastly, you should prefer the make_pair utility function for making pairs:
m.insert(std::make_pair(k1,v1));
m.insert(std::make_pair(k2,v2));
It saves you from having to write out the types for the pair, and is generally easier to read. (Especially when longer type names come along.)
Signature of the less than operator needs to be bool operator<(const _pos& other) const, otherwise map can not use this operator in const functions since this member function is declared as non-const.
I think that your definition of operator< is wrong - the right hand side (argument in this case) should be marked const and it should be a const member function, e.g.
bool operator<(const _pos& other) const{
return this->xi < other.xi;
}