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;
}
Related
I have a bunch of objects in a class hierarchy and would like to make a std::map using references to those objects as the keys in the map. Its seems like std::reference_wrapper would be exactly what is needed for this, but I can't seem to make it work. What I've tried so far:
class Object { // base class of my hierarchy
// most details unimportant
public
virtual bool operator< (const Object &) const; // comparison operator
};
std::map<std::reference_wrapper<const Object>, int> table;
auto it = table.find(object);
table[object] = 42;
table[object]++
However, I always get somewhat obscure errors from the compiler:
/usr/include/c++/4.5.3/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = std::reference_wrapper<const Object>]’:
/usr/include/c++/4.5.3/bits/stl_tree.h:1522:38: instantiated from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::find(const _Key&) [with _Key = std::reference_wrapper<const Object>, _Val = std::pair<const std::reference_wrapper<const Object>, int>, _KeyOfValue = std::_Select1st<std::pair<const std::reference_wrapper<const Object>, int> >, _Compare = std::less<std::reference_wrapper<const Object> >, _Alloc = std::allocator<std::pair<const std::reference_wrapper<const Object>, int> >, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::reference_wrapper<const Object>, int> >]’
/usr/include/c++/4.5.3/bits/stl_map.h:697:29: instantiated from ‘std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&)[with _Key = std::reference_wrapper<const Object>, _Tp = int, _Compare = std::less<std::reference_wrapper<const Object> >, _Alloc = std::allocator<std::pair<const std::reference_wrapper<const Object>, int> >, std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::reference_wrapper<const Object>, int> >, key_type = std::reference_wrapper<const Object>]’
testfile.cpp:39:31: instantiated from here
/include/c++/4.5.3/bits/stl_function.h:230:22: error: no match for ‘operator<’ in ‘__x < __y’
The error seems to be saying it can't compare two std::reference_wrapper<const Object> objects, but it seems like it should be possible -- std::reference_wrapper has a conversion operator that can implicitly convert it to a T& (const Object & here), and Object has a operator <, so why doesn't it work?
Should it work and this is merely a bug in g++? Or is something else going on?
By default std::map uses std::less<std::reference_wrapper<const Object>> as Compare, but std::reference_wrapper<T> doesn't forward operator<() to the underlying type T.
The simplest and concisest option to solve your problem is to define std::less<const Object> (or std::greater<const Object>) in the map definition like this:
std::map<std::reference_wrapper<const Object>, int, std::less<const Object>> table;
It will work correctly and as expected due to implicit conversion of std::reference_wrapper to T& and implicit constructor of std::reference_wrapper.
Example.
It seems that it would work if you made the comparison operator a free function (that perhaps calls a virtual member function).
If it is a member function, a < b really means a.operator<(b); and implicit conversions are not considered for the left-side argument.
On Visual Studio 11 Beta, I get the same problem.
Using the free version which calls the < operator solves the problem.
#include<map>
#include<iostream>
using namespace::std;
class Object {
int _n1;
public:
Object(int n = 0):_n1(n){};
bool operator < (const Object& rhs) const {return this->_n1 < rhs._n1;}
friend ostream &operator << (ostream &stream, const Object& o) { stream << o._n1 << " "; return stream;}
};
struct ObjectLess{
bool operator()(const Object& lhs, const Object& rhs) const
{
return lhs<rhs;
}
};
int main(int argc, char* argv[])
{
//This does not compile
//std::map<std::reference_wrapper<const Object>, string> table;
//Using the free function works
std::map<std::reference_wrapper<const Object>, string, ObjectLess> table;
Object a(1);
Object b(2);
Object c(3);
table[a]="One";
table[c]="Three";
table[b]="Two";
for(auto y: table){
cout << y.first << " " << y.second.c_str() << std::endl;
}
return 0;
}
I'm getting a lot of use of deleted function error. I just changed the pointer of weighted_pointer to unique_ptr. But I can't realize why I'm getting the error, any tip?
The likeatree is a DAG structure which can point to another struct or an element of stdDeque based on mask value.
The weight of weighted_pointer has mutable keyword because doesn't change where in the set will be.
#include <deque>
#include <set>
#include <vector>
#include <iostream>
#include <algorithm>
#include <memory>
#include <chrono>
using namespace std;
struct likeatree{
unsigned int mask : 3;
void * a;
void * b;
};
struct weighted_pointer{
mutable int weight;
unique_ptr<likeatree> ptr;
};
struct ptrcomp{
bool operator()(const weighted_pointer & lhs, const weighted_pointer & rhs) {
if(lhs.ptr->mask < rhs.ptr->mask)
return true;
if(lhs.ptr->mask > rhs.ptr->mask)
return false;
if(lhs.ptr -> a < rhs.ptr->a)
return true;
if(lhs.ptr->a > rhs.ptr->a)
return false;
return lhs.ptr->b < rhs.ptr->b;
}
};
vector<likeatree *> treeVector;
deque<bool> stdDeque(3);
vector<vector<bool>> boolMatrix{{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
set<weighted_pointer,ptrcomp> stdSet;
int main(){
srand(time(NULL));
likeatree * first_pointer = new likeatree{0,&input[0],nullptr};
likeatree * second_pointer = first_pointer;
unique_ptr<likeatree> tmp(first_pointer);
weighted_pointer wp;
wp.weight = 1;
wp.pointer = move(tmp);
stdSet.insert(move(wp));
// I'd like to do it inline(or more but with variables that end of scope here), but this don't work. (And i don't keep a copy of the pointer)
// stdSet.insert(move(weighted_pointer{1,move(make_unique<likeatree>(*new likeatree{0,&input[0],nullptr}))}));
return 0;
}
Edit: Changed code with a single case of the problem
Edit: Solved. Missing a dereference when using make_unique.
Your struct here:
struct weighted_pointer{
mutable int weight;
unique_ptr<likeatree> ptr;
};
contains a std::unique_ptr. A std::unique_ptr cannot be copied, so your entire weighted_pointer cannot be copied as well.
There are three places in your code where you attempt to copy it, which causes the errors you see:
bool operator()(const weighted_pointer lhs, const weighted_pointer rhs) {
Must be:
bool operator()(weighted_pointer const& lhs, weighted_pointer const& rhs) {
stdSet.insert(tmp);
This could theoretically be fixed by:
stdSet.insert(std::move(tmp));
However, you then cannot use tmp anymore, which you do, not only in the same loop but also in the loop below. So you must find a different solution altogether. Perhaps use emplace. Or restructure your code entirely.
auto it = find_if(stdSet.begin(),stdSet.end(),[&](weighted_pointer temp){ return temp.ptr.get() == treeVector[i]; });
Must be:
auto it = find_if(stdSet.begin(),stdSet.end(),[&](weighted_pointer const& temp){ return temp.ptr.get() == treeVector[i]; });
For VC++ 2013, the std::move fix will not suffice. You will have to add an explicit move constructor to your struct:
struct weighted_pointer{
mutable int weight;
unique_ptr<likeatree> ptr;
weighted_pointer() = default;
weighted_pointer(weighted_pointer&& src) :
weight(std::move(src.weight)),
ptr(std::move(src.ptr))
{
}
};
VC++ 2015 fixes this problem. More information: Default Move Constructor in Visual Studio 2013 (Update 3)
Your weighted_pointer is non-copyable because it contains a non-copyable member (the unique_ptr), so you have to pass it by const reference to your comparator function.
bool operator()(const weighted_pointer& lhs, const weighted_pointer& rhs)
This is because if you pass it by value (as it is currently written), it will try to make a function-local copy.
You also cannot do this because you are trying to copy tmp, which as I just said that struct is non-copyable.
for(unsigned int i = 0; i < stdDeque.size(); i++){
tmp.ptr.reset(new likeatree{0,&stdDeque[i],nullptr});
stdSet.insert(tmp);
}
You can use emplace to construct a weighted_pointer in-place instead
for(unsigned int i = 0; i < stdDeque.size(); i++){
stdSet.emplace(1, std::make_unique<likeatree>(0,&stdDeque[i],nullptr));
}
When I compile the code above, the compiler says:
In file included from /usr/include/c++/4.8/algorithm:62:0,
from 31791982.cpp:7:
/usr/include/c++/4.8/bits/stl_algo.h: In instantiation of ‘_InputIterator std::__find_if(_InputIterator, _InputIterator, _Predicate, std::input_iterator_tag) [with _InputIterator = std::_Rb_tree_const_iterator<weighted_pointer>; _Predicate = main()::__lambda0]’:
/usr/include/c++/4.8/bits/stl_algo.h:4465:41: required from ‘_IIter std::find_if(_IIter, _IIter, _Predicate) [with _IIter = std::_Rb_tree_const_iterator<weighted_pointer>; _Predicate = main()::__lambda0]’
31791982.cpp:55:124: required from here
So look at line 55 - what's happening:
auto it = find_if(stdSet.begin(),stdSet.end(),[&](weighted_pointer temp){ return temp.ptr.get() == treeVector[i]; });
We're trying to copy a weighted_pointer from the array into the lambda's temp. But really, we'd be happy with a const ref, so replace with const weighted_pointer& and compile again:
/usr/include/c++/4.8/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 = weighted_pointer; _Val = weighted_pointer; _KeyOfValue = std::_Identity<weighted_pointer>; _Compare = ptrcomp; _Alloc = std::allocator<weighted_pointer>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::key_type = weighted_pointer]’:
/usr/include/c++/4.8/bits/stl_tree.h:1377: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 = const weighted_pointer&; _Key = weighted_pointer; _Val = weighted_pointer; _KeyOfValue = std::_Identity<weighted_pointer>; _Compare = ptrcomp; _Alloc = std::allocator<weighted_pointer>]’
/usr/include/c++/4.8/bits/stl_set.h:463:29: required from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename _Alloc::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(const value_type&) [with _Key = weighted_pointer; _Compare = ptrcomp; _Alloc = std::allocator<weighted_pointer>; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename _Alloc::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<weighted_pointer>; std::set<_Key, _Compare, _Alloc>::value_type = weighted_pointer]’
31791982.cpp:49:26: required from here
Line 49 is:
stdSet.insert(tmp);
We can't copy tmp into the set. If we weren't going to re-use tmp we could move it instead:
for(unsigned int i = 0; i < stdDeque.size(); i++){
weighted_pointer tmp;
tmp.weight = 1;
tmp.ptr.reset(new likeatree{0,&stdDeque[i],nullptr});
stdSet.insert(std::move(tmp));
}
That then leaves us with an easy fix for ptrcomp::operator() which needs to accept its arguments by const reference.
I'm looking for a way to find an element inside a map using the base class (the code bellow is just a basic example):
#include <map>
#include <boost/shared_ptr.hpp>
class Base {
public:
Base(int v) : id(v) {};
int id;
};
class Derived : public Base {
public:
Derived(int v) : Base(v) {};
};
int main()
{
std::map<boost::shared_ptr<Derived>, double> m;
m.insert(std::make_pair(boost::shared_ptr<Derived>(new Derived(1)), 10));
m.insert(std::make_pair(boost::shared_ptr<Derived>(new Derived(2)), 20));
auto b1 = boost::shared_ptr<Base>(new Base(1));
m.find(b1);
return 0;
}
Basically, I want to compare the id attribute. The errors returned by the compiler are the following:
main.cpp: In function 'int main()':
main.cpp:35:14: error: no matching function for call to 'std::map<boost::shared_ptr<Derived>, double>::find(boost::shared_ptr<Base>&)'
m.find(b1);
^
main.cpp:35:14: note: candidates are:
In file included from /usr/include/c++/4.8/map:61:0,
from main.cpp:1:
/usr/include/c++/4.8/bits/stl_map.h:820:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&) [with _Key = boost::shared_ptr<Derived> _Tp = double; _Compare = std::less<boost::shared_ptr<Derived> > _Alloc = std::allocator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::key_type = boost::shared_ptr<Derived>]
find(const key_type& __x)
^
/usr/include/c++/4.8/bits/stl_map.h:820:7: note: no known conversion for argument 1 from 'boost::shared_ptr<Base>' to 'const key_type& {aka const boost::shared_ptr<Derived>&}'
/usr/include/c++/4.8/bits/stl_map.h:835:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&) const [with _Key = boost::shared_ptr<Derived> _Tp = double; _Compare = std::less<boost::shared_ptr<Derived> > _Alloc = std::allocator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator = std::_Rb_tree_const_iterator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::key_type = boost::shared_ptr<Derived>]
find(const key_type& __x) const
^
/usr/include/c++/4.8/bits/stl_map.h:835:7: note: no known conversion for argument 1 from 'boost::shared_ptr<Base>' to 'const key_type& {aka const boost::shared_ptr<Derived>&}'
If you want to use your map for lookup by id, you need to pass in an appropriate comparison function so that the map sorts its keys by id instead of the default operator < (which, I believe, compares ownership block addresses with boost::shared_ptr arguments).
So change the map like this:
struct Less_id
{
bool operator() (const boost::shared_ptr<Derived> &lhs, const boost::shared_ptr<Derived> &rhs) const
{
return lhs->id < rhs->id;
}
};
typedef std::map<boost::shared_ptr<Derived>, double, Less_id> Map;
Map m;
This will sort the map accordingly, but still not allow lookup by Base pointer. To do that, you can write your own function above std::lower_bound:
Map::const_iterator find_base(const Map &map, const boost::shared_ptr<Base> &base)
{
auto it = std::lower_bound(
map.begin(), map.end(), base,
[](const Map::value_type &lhs, const boost::shared_ptr<Base> &rhs)
{ return lhs.first->id < rhs->id; }
);
if (it != map.end() && it->first->id == base->id)
return it;
else
return map.end();
}
std::lower_bound() is used to keep the logarithmic complexity std::map::find() offers.
Live example
Use an
std::map<boost::shared_ptr<Derived>, double, std::less<boost::shared_ptr<Base>>>
Edit: I am ahead of the times - this only works for C++14.
There are two issues to overcome. The first is that you want to search for a Base in a collection of Deriveds. The second is that you want to compare by value rather than by address. The other answers are neglecting this second point. Try std::find_if:
auto b1 = boost::shared_ptr<Base>(new Base(1));
auto itFound = std::find_if(m.begin(), m.end()
[=](const std::pair<boost::shared_ptr<Derived>, double>& pair)
{
// omitting null checks for this example
return pair.first->id == b1->id;
});
And if the requirement is really just to find a key with the given id, you could make it simpler:
int queryKey = 1;
auto itFound = std::find_if(m.begin(), m.end()
[=](const std::pair<boost::shared_ptr<Derived>, double>& pair)
{
return pair.first->id == queryKey;
});
Now, as noted in the comments, this will give you linear rather than map's usual logarithmic lookup time. If the map is small it won't matter, but if this is an issue, you could use std::lower_bound instead of find_if. Note that this would also require adding a custom comparer so you could ensure the map's sort order was based on id. For example:
struct Compare
{
bool operator()(const boost::shared_ptr<Derived>& l,
const boost::shared_ptr<Derived>& r) const
{
// omitting null checks for this example
return l->id < r->id;
}
};
std::map<boost::shared_ptr<Derived>, double, Compare> m;
This is because boost::shared_ptr's operator < is not what you want, you need a delegation to the Derived class's operator <.
use std::map<_Key, _Tp, _Compare>
for example:
std::map<boost::shared_ptr<Derived>, double, compare_func()> m;
As you are looking for an object Base which can only be in the map if it is of type Derived you can simply do this:
boost::shared_ptr<Derived> d1 = boost::dynamic_pointer_cast<Derived>(b1);
if(d1) {
m.find(d1);
} else {
// not in the map
}
As you use std::shared_ptr<Derived> as a key, another possibility would be to actually use pointers to the base class instead:
Use
std::map<boost::shared_ptr<Base>, double> m;
instead of
std::map<boost::shared_ptr<Derived>, double> m;
and everything works as expected.
Btw, you are missing a virtual destructor in Base!
This question already has answers here:
C++ Converting function pointer to unique “hash” key
(2 answers)
Closed 9 years ago.
I am writing in C++, trying to compile under Ubuntu, and I am experiencing some issues with a map using function pointers as keys. When I define the map, I get no compiling errors, but as soon as I try to insert an element, I get a rather wordy
In file included from /usr/include/c++/4.6/string:50:0,
from /usr/include/c++/4.6/bits/locale_classes.h:42,
from /usr/include/c++/4.6/bits/ios_base.h:43,
from /usr/include/c++/4.6/ios:43,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from main.cpp:1:
/usr/include/c++/4.6/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = int (MyClass::*)()]’:
/usr/include/c++/4.6/bits/stl_tree.h:1277:4: instantiated from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = int (MyClass::*)(), _Val = std::pair<int (MyClass::* const)(), std::vector<int> >, _KeyOfValue = std::_Select1st<std::pair<int (MyClass::* const)(), std::vector<int> > >, _Compare = std::less<int (MyClass::*)()>, _Alloc = std::allocator<std::pair<int (MyClass::* const)(), std::vector<int> > >]’
/usr/include/c++/4.6/bits/stl_map.h:518:41: 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::map<_Key, _Tp, _Compare, _Alloc>::value_type>::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const value_type&) [with _Key = int (MyClass::*)(), _Tp = std::vector<int>, _Compare = std::less<int (MyClass::*)()>, _Alloc = std::allocator<std::pair<int (MyClass::* const)(), std::vector<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_type>::other>::iterator = std::_Rb_tree_iterator<std::pair<int (MyClass::* const)(), std::vector<int> > >, std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair<int (MyClass::* const)(), std::vector<int> >]’
main.cpp:36:51: instantiated from here
/usr/include/c++/4.6/bits/stl_function.h:236:22: error: invalid operands of types ‘int (MyClass::* const)()’ and ‘int (MyClass::* const)()’ to binary ‘operator<’
Here is the example that caused the above error message:
#include <iostream>
#include <map>
#include <vector>
// class definition
class MyClass
{
public:
int f1(void);
int f2(void);
};
int MyClass::f1(void)
{
return 1;
}
int MyClass::f2(void)
{
return 2;
}
using namespace std;
int main( int argc, char* argv[] )
{
// define map
map< int (MyClass::*)(void), vector<int> > myMap;
vector<int> myVector;
//myMap[ &MyClass::f1 ] = myVector;
myMap.insert( make_pair( &MyClass::f1, myVector) );
return 0;
}
What could be the issue? I tried with both insert and [] assign, and I get the same error. Browsing the forums, I found this; but could that be the issue? I don't think I need to define an operator "<" for function pointers (shouldn't they behave as regular pointers?) ...or do I?
The error is telling you all you need to know:
invalid operands of types ‘int (MyClass::* const)()’ and ‘int (MyClass::* const)()’ to binary ‘operator<’
You cannot compare member function pointers using standard operator<, so you must provide a custom comparator when declaring your map.
Unfortunately, pointers to member functions cannot be compared for inequality, so you cannot define a comparison operator or use a std::mapin this case. I suggest using std::unordered_map, which only needs a std::hash and equality comparison, which you can do. See here for hashing and here for equality comparison.
You could implement the template specialization for less< int (MyClass::* const)() >, like follows:
typedef int (MyClass::*tMyClassMember)();
namespace std {
template<>
struct less<tMyClassMember>
{
bool operator()(const tMyClassMember& k1, const tMyClassMember& k2) const
{
auto p1 = reinterpret_cast<const intptr_t*>(&k1);
auto p2 = reinterpret_cast<const intptr_t*>(&k2);
return *p1 < *p2;
}
};
}
There may be better ways to compare pointer-to-members than "casting" them to integers, which is an implementation-specific hack, according to this question. That questions contains details about how to do that.
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