Reputation: 126328
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?
Upvotes: 11
Views: 8789
Reputation: 4997
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
.
Upvotes: 16
Reputation: 1035
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;
}
Upvotes: 1
Reputation: 41341
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.
Upvotes: 9