How can I insert a structure as key into a map?
I'm getting a compiling error for the code below, aft开发者_运维技巧er removing the comment characters from the first insert
line. I'm unable to insert the structure into the map while inserting integers is fine.
# include <iostream>
# include <map>
using namespace std;
struct node
{int test;} temp;
int main()
{
temp.test = 24;
int test = 30;
map<node, bool> mymap1;
map<int, bool> mymap2;
//mymap1.insert(make_pair(temp, true));
mymap2.insert(make_pair(test, true));
return 0;
}
How can I fix the error?
For a type to serve as the key for a map, it has to be ordered. All that means, practically, is that operator<
must be defined for the type. If you defined a global operator<(const node&, const node&)
, this should work fine; i.e.,
bool operator<(const node& n1, const node& n2) {
return n1.test < n2.test;
}
A std::map's keys are stored internally in a binary search tree. In order for keys to be stored and searched for in a binary search tree, they must be comparable. For example, a requirement of a binary search tree is that a left child's key is less than its parent's key and a right child's key is greater than its parent's key. However, if the keys are not comparable, how are we supposed to tell whether the children are greater or less than the parent? We cannot form a tree and therefore std::map will not work with these types.
You simply need to define the less than operator like so:
bool operator<(const node& n1, const node& n2)
{
return n1.test < n2.test;
}
This also must be a friend of your node struct if the "test" data member is private (It's public now since node is currently a struct). However, I would probably do it like this:
#include <map>
class node
{
public:
int getTest() const { return _test; }
void setTest(int test) { _test = test; }
private:
int _test;
};
bool operator<(const node& n1, const node& n2)
{
return n1.getTest() < n2.getTest();
}
int main()
{
std::map<node,bool> foo;
node n;
n.setTest(25);
foo[n] = true;
return 0;
}
Here's how to read the error messages:
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_tree.h:1141: 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 = node, _Val = std::pair<const node, bool>, _KeyOfValue = std::_Select1st<std::pair<const node, bool> >, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_map.h:469: 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 = node, _Tp = bool, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
prog.cpp:15: instantiated from here
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
First, we ignore most of the 'instantiated from' lines, because they're just talking about how the templates got expanded. The important one is the last one, referring to our source code, because it tells us where the error was triggered. Of course, we knew that anyway, so we'll skip that too. We'll also ignore the path to the library header in question, because we don't really care how the compiler stores its stuff.
stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
So... our code indirectly calls ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’
, or if we actually do that substitution, ‘bool std::less<node>::operator()(const node&, const node&) const’
. And this is a problem because there is no match for ‘operator<’ in ‘__x < __y’
.
__x
and __y
are variables inside the std::less
implementation (you should be able to guess that much). From the name, we can guess (and if we had studied the standard library, we would know) that std::less
is a template function that compares two things of the same type and returns whether or not the first is less than the second.
How does it do that? By using the operator<
, of course. So that's what we need to do to fix the problem: it says the operator<
isn't there for what's being compared, so we have to provide it. What's being compared? node
s, of course. So we define operator<
for our class.
Why does it do that? So that we can write functions that accept a comparison-operation as an argument (either a template argument or a run-time parameter - but the former is far more common), and pass std::less
. That's the reason for std::less
's existence: it turns the act of comparing things into a function, and actual functions are somewhat more useful.
How is that relevant? Because, like the others said, std::map actually is passing std::less
as an argument. It's actually a default argument to the std::map
template that's used to compare elements. After all, part of the interface of a map is that every key is unique. How are you going to check keys for uniqueness if you can't compare them? Granted, technically you would only have to compare them for equality for that to work. But it turns out that being able to order the keys makes it possible to create a much more efficient data structure. (You would know about this if you actually took courses in university about programming and CS.)
Why wasn't there a problem with int
? You should be able to guess by now: operator<
already naturally works for int
s. But you have to tell C++ how to do it for any user types, because you might have something else in mind.
C++11
As mentioned in Andrew Rasmussen's answer, the keys of a std::map
must be comparable. However, you can also provide a custom comparison object to your map instead of defining operator<
for your struct. Moreover, since C++11, you can use a lambda expression instead of defining a comparison object. As a result, you can keep your code as short as follows:
auto comp = [](const node& n1, const node& n2) { return n1.test < n2.test; };
std::map<node, bool, decltype(comp)> mymap1(comp);
Code on Ideone
精彩评论