473,394 Members | 1,739 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,394 software developers and data experts.

std::map< MyString, MyString > comparison operator?

This is probably a very obvious question, but I'm not clear on what
operators need to be implemented for std::map.find() to work. For
example, I have a class MyString that wraps std::string, and which also
implements ==, <, <=, >, >=, etc. (Those operators are tested and
working correctly.)

If I assign map["hello"] = "world", it saves the MyString's correctly
in the map. But a subsequent call to map.find("hello") returns
map.end(). Even more bizarre, a cout << on destruction suggests that a
subsequent map["hello"] = "something else" results in TWO items for
map["hello"]... How is that possible? I thought that was only a
behavior of multimap, but I suspect it's related to my MyString
implementation not handling comparisons properly.

Changing the map to < string, string > then works correctly, finds the
item, etc.

So the question is, what does <string> implement that <MyString> needs
in order to work as a Key in a std::map? Thanks in advance for your
help.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #1
13 4559

jstanforth wrote:
....

So the question is, what does <string> implement that <MyString> needs
in order to work as a Key in a std::map? Thanks in advance for your
help.


bool operator<( .. ) const

Is all you need to declare.

It sounds like you have a problem with your comparison operator or you
copy constructor.
Jul 23 '05 #2
jstanforth wrote:
This is probably a very obvious question, but I'm not clear on what
operators need to be implemented for std::map.find() to work. For
example, I have a class MyString that wraps std::string, and which also
implements ==, <, <=, >, >=, etc. (Those operators are tested and
working correctly.)

If I assign map["hello"] = "world", it saves the MyString's correctly
in the map. But a subsequent call to map.find("hello") returns
map.end().
In addition to what the other poster said, I suspect that you forgot
to define a copy constructor and/or operator= on your class MyString.
std::map makes copies of your objects, that’s why you need them,
That could explain why map.find(“hello”) returns map.end().
Even more bizarre, a cout << on destruction suggests that a
subsequent map["hello"] = "something else" results in TWO items for
map["hello"]... How is that possible? I thought that was only a
behavior of multimap, but I suspect it's related to my MyString
implementation not handling comparisons properly.

It's normal that the destructor is called twice, because the second
assignment will destroy the first one, remember that the map has
copies.

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #3
Hello "jstanforth"

jstanforth schrieb:
This is probably a very obvious question, but I'm not clear on what
operators need to be implemented for std::map.find() to work. For
example, I have a class MyString that wraps std::string, and which also
implements ==, <, <=, >, >=, etc. (Those operators are tested and
working correctly.)

If I assign map["hello"] = "world", it saves the MyString's correctly
in the map. But a subsequent call to map.find("hello") returns
map.end(). Even more bizarre, a cout << on destruction suggests that a
subsequent map["hello"] = "something else" results in TWO items for
map["hello"]... How is that possible? I thought that was only a
behavior of multimap, but I suspect it's related to my MyString
implementation not handling comparisons properly.

Changing the map to < string, string > then works correctly, finds the
item, etc.

So the question is, what does <string> implement that <MyString> needs
in order to work as a Key in a std::map? Thanks in advance for your
help.

You should provide a complete (short) test program, otherwise the
community has no chance to
help you. From your description it seems, there could be many reasons of
error. A a simple list
of possibilities:

1) You have specialized std::less for your MyString class in a way
violating strictly weak ordering
requirements.
Note: This functor is used in std::map, if you don't provide a
special comparator. The standard
implementation of std::less<MyString> operator() will use operator<.
2) At least your implementation of operator< of your MyString class does
not work correctly, although
you claim it should work. This cannot be argued without its
implementation, but you can easily test it:
Just ensure that it obeys the strictly weakly comparable
requirements, which are:
(a) (x < x) == false
(b) if x < y, than !(y < x)
(c) if x < y and y < z than x < z
(d) Equivalence: If x and y are equivalent, than (x < y) == false
and (y < x) == false
3) Your test program causes undefined behaviour or tests the wrong thing.

Greetings from Bremen,

Daniel Krügler
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #4
"jstanforth" <js********@gmail.com> wrote in message news:<11**********************@f14g2000cwb.googleg roups.com>...
This is probably a very obvious question, but I'm not clear on what
operators need to be implemented for std::map.find() to work. For
example, I have a class MyString that wraps std::string, and which also
implements ==, <, <=, >, >=, etc. (Those operators are tested and
working correctly.)

If I assign map["hello"] = "world", it saves the MyString's correctly
in the map. But a subsequent call to map.find("hello") returns
map.end(). Even more bizarre, a cout << on destruction suggests that a
subsequent map["hello"] = "something else" results in TWO items for
map["hello"]... How is that possible? I thought that was only a
behavior of multimap, but I suspect it's related to my MyString
implementation not handling comparisons properly.

Changing the map to < string, string > then works correctly, finds the
item, etc.

So the question is, what does <string> implement that <MyString> needs
in order to work as a Key in a std::map? Thanks in advance for your
help.


Have you implemented the assignment operator and copy constructor for
the MyString class? If you are storing MyString by value in the map
these are required.

The only operator required to store UDTs in an ordered container is
the < operator, which you have already defined. The other comparison
operators can be implemented in terms of these.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #5
Thank you, each of you--- I really appreciate your generous help with
this. I was initially going to post a test example except that it
relies on the MyString definition, etc. and I don't see a manageable
(for you folks) way to attach files here without posting 7K of text
into a message (which is tough for you to read).

So for starters, perhaps I can just use excerpts where the problem
likely lies, namely the copy constructor and overloaded operator<. I'm
happy to email compilable source code to anyone interested or provide
any other information. But hopefully I'm just making some simple
mistake in these specific areas....

- Imagine XString as a class with std::string _container as a private
member.
- The copy constructor, operator<, and operator= are defined as
follows:

XString::XString(XString& str) :
_container( str._container )
{

};

bool XString::operator<(const XString& str)
{
return (_container < str._container);
};

// assignment operator from a char*
XString& XString::operator=(const char* cs)
{
_container = cs;
return *this;
};

// assignment operator from an STL std::string
XString& XString::operator=(const std::string& str)
{
_container = str;
return *this;
};

// assignment operator from another XString
XString& XString::operator=(const XString& str)
{
_container = str._container;
return *this;
};
Then the test code (in a main() block) looks like this:

map< XString, XString > xsmap;
map< XString, XString >::iterator xsmapItr;

XString t1("john");
XString t2("annakin");
XString t3("george");

xsmap["hello"] = "world";
xsmap[t1] = "adams";
xsmap[t2] = "skywalker";
xsmap[t3] = "lucas";
xsmap["john"] = "doe";
xsmap[t1] = "something else";

xsmapItr = xsmap.find("hello");

if ( xsmapItr == xsmap.end() )
cout << "xstring not found" << endl;

== OUTPUTS: xstring not found
for ( xsmapItr = xsmap.begin() ; xsmapItr != xsmap.end();
++xsmapItr )
cout << xsmapItr->first << " : " << xsmapItr->second << endl;

==OUTPUTS:
john : adams
hello : world
annakin : skywalker
george : lucas
john : something else
john : doe
cout << t1 << " < " << t2 << " : " << ( t1 < t2 ? true : false ) <<
endl;
cout << t1 << " < " << t3 << " : " << ( t1 < t3 ? true : false ) <<
endl;

cout << t2 << " < " << t1 << " : " << ( t2 < t1 ? true : false ) <<
endl;
cout << t2 << " < " << t3 << " : " << ( t2 < t3 ? true : false ) <<
endl;

cout << t3 << " < " << t1 << " : " << ( t3 < t1 ? true : false ) <<
endl;
cout << t3 << " < " << t2 << " : " << ( t3 < t2 ? true : false ) <<
endl;

==OUTPUTS:
john < annakin : 0
john < george : 0
annakin < john : 1
annakin < george : 1
george < john : 1
george < annakin : 0
So... note the correct results for the < tests, plus note that
iterating through the map shows three results for "john".... (ie. this
isn't a destruction-on-copy operation as a previous poster suggested,
but all three exist in the map simultaneously). But all assignments
and comparisons seem to work correctly, suggesting the copy constructor
and operator< are working as expected. Also, I have operators and
methods all provide for const char* cs, std::string& str, and XString&
str for all possible operations.

Thanks so much for your help with this. Hopefully I'm just missing
something obvious here...
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #6
jstanforth wrote:
Thank you, each of you--- I really appreciate your generous help with
this. I was initially going to post a test example except that it
relies on the MyString definition, etc. and I don't see a manageable
(for you folks) way to attach files here without posting 7K of text
into a message (which is tough for you to read).

So for starters, perhaps I can just use excerpts where the problem
likely lies, namely the copy constructor and overloaded operator<. I'm
happy to email compilable source code to anyone interested or provide
any other information. But hopefully I'm just making some simple
mistake in these specific areas....

- Imagine XString as a class with std::string _container as a private
member.
- The copy constructor, operator<, and operator= are defined as
follows:

[snip]

I tried the following, using your source, and some minor mods.
And, it works just right. What compiler are you using?

Note:

1. The default constructor.
2. The 'const' operator<.
3. The 'const char *' constructor.
4. operator<< overload for ostream.

Copy and assignment were left pretty much as-is.

This is merely an algorithmic check; so, I didn't bother with
encapsulation at all.
--
A. Kanawati
NO*************@comcast.net
#ifndef XString_dot_h_
#define XString_dot_h_

#include <string>
#include <iosfwd>

struct XString {
std::string _container;

XString();
XString(const XString &);
XString(const char *);

XString &operator=(const XString &);
XString &operator=(const std::string &);
XString &operator=(const char *);

bool operator< (const XString &) const;
bool operator< (const XString &);
};

inline XString::XString() : _container()
{
}

inline
XString::XString(const char *s) : _container(s)
{
}

inline
XString::XString(const XString& str) :
_container( str._container )
{

}

inline
bool XString::operator<(const XString& str)
{
return (_container < str._container);
}

inline
bool XString::operator<(const XString& str) const
{
return (_container < str._container);
}

// assignment operator from a char*
inline
XString& XString::operator=(const char* cs)
{
_container = cs;
return *this;
}

// assignment operator from an STL std::string
XString& XString::operator=(const std::string& str)
{
_container = str;
return *this;
}

// assignment operator from another XString
inline
XString& XString::operator=(const XString& str)
{
_container = str._container;
return *this;
}

inline std::ostream &operator<<(std::ostream & o, const XString &str)
{
return o << str._container;
}

#endif

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #7

"jstanforth" <js********@gmail.com> wrote in message
news:11**********************@o13g2000cwo.googlegr oups.com...
XString::XString(XString& str) :
_container( str._container )
{

};


1: the proper form of a copy constructor is *usually*:

T::T(const T& rhs); // notice the const
2: do not name data members starting with an underscore. 17.4.3.1.2.1:

- Each name that contains a double underscore (_ _) or begins with an
underscore followed by an uppercase letter (2.11) is reserved to the
implementation for any use.

- Each name that begins with an underscore is reserved to the implementation
for use as a name in the global namespace.
I'd like to see your class definition. I suspect that either your
"_container" is declared static, or some other issue with the class
declaration.

Here is an idea for you. You should be able to create a very small class
XString. All you should need is the constructors (default, copy,
construct-from-const char*), and operator<. Then, your above test code
should work correctly.

Trim the class XString down as small as possible and repost the entire
class, declaration AND definition. We can then get to the bottom of your
problem.
joshua lehrer
factset research systems
NYSE:FDS

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Jul 23 '05 #8

"Antoun Kanawati" <an*****@comcast.net> wrote in message
news:HL********************@comcast.com...
bool operator< (const XString &) const;
bool operator< (const XString &);

Why the overloaded operator<? The first one should suffice.

joshua lehrer
factset research systems
NYSE:FDS

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #9

jstanforth wrote:
bool XString::operator<(const XString& str)
{
return (_container < str._container);
};


Make this member const. If the map is doing a comparison with const
'this', then something else is being called. Perhaps XString has an
implicit conversion to const char * (in which case you are comparing
text locations, not values) or perhaps to something else (I've seen
string classes with implicit conversion to double).

Provide a complete listing of all your XString members (as tested) and
we may be able to figure this out.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #10
Joshua Lehrer wrote:
"Antoun Kanawati" <an*****@comcast.net> wrote in message
news:HL********************@comcast.com...
bool operator< (const XString &) const;
bool operator< (const XString &);
Why the overloaded operator<? The first one should suffice.


The second (non-const) is left-over from the original posting;
I didn't try to make the code elegant, or encapsulated; just
tweaked enough to compile and test.

The net result: I have no idea why maps are not working for the
OP.
--
A. Kanawati
NO*************@comcast.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #11
Joshua Lehrer wrote:
2: do not name data members starting with an underscore. 17.4.3.1.2.1:
- Each name that contains a double underscore (_ _) or begins with an
underscore followed by an uppercase letter (2.11) is reserved to the
implementation for any use.
Okay, the second character wasn't uppercase and there wasn't a double
underscore.
- Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.


Okay, members are not in the global namespace.

It should be okay to use a name starting with _ as a member, provided
that
the next character is a digit or lowercase letter.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #12
NICE CALL on making the member const! I'd already made a simplified
XString object with just the few members (used in the test case posted
a little earlier), and making this member const makes the sample output
EXACTLY as expected, no more duplicates. And yes, there is an implicit
conversion happening--- very good guess!

Thanks so much to everyone for your help... You guys are great. The
fact that you're debugging this abstractly and still finding the
problem I couldn't find is seriously impressive. (And I'd tried almost
all the suggestions except this one before even posting here, so I was
about ready to lose hope and code around it...) Thanks again.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #13

jstanforth wrote:
NICE CALL on making the member const!


Glad to help. Before you get too happy, I strongly recommend that you
make sure you understand why std::basic_string has "c_str()" instead of
"operator const char*()" and also why its operator<() is a non-member
function.

I believe section 11 of TC++PL (3rd edition) has some pertinent
discussion.

Production quality SomeString classes that do provide operator const
char*(), will typically provide multiple operator<() declarations.
See, for example,
http://msdn.microsoft.com/library/de..._operators.asp
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 23 '05 #14

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: gipsy boy | last post by:
// -- using namespace std; map<string,string> formMap; list<string> formParams; for(list<string>::iterator fit = formParams.begin(); fit!=formParams.end(); fit++) { cout << "key=" << *fit;...
5
by: Peter Jansson | last post by:
Hello, I have the following code: std::map<int,std::set<std::string> > k; k="1234567890"; k="2345678901"; //... std::set<std::string> myMethod(std::map<int,std::set<std::string> > k)...
2
by: qfel | last post by:
If new pair is inserted into std::map through operator, is default value for build-in types equal to 0? eg. std::map<std::string,bool> m; bool b=m; will b equal to false, or will it contain...
19
by: Erik Wikström | last post by:
First of all, forgive me if this is the wrong place to ask this question, if it's a stupid question (it's my second week with C++), or if this is answered some place else (I've searched but not...
1
by: Maxwell | last post by:
Hello, I having having oodles of trouble using the std lib in my MC++ (VS.NET 2003) Class library. I figured out a simple sample to reproduce the errors I am having. Create a MC++ (VS.NET 2003)...
4
by: lada77 | last post by:
All, Just wondering if one of you very helpful guru's out there could comment on some odd behaviour I am seeing. I'm betting it is something obvious but I am not experienced enough to tell...
2
by: brzozo2 | last post by:
Hello, this program might look abit long, but it's pretty simple and easy to follow. What it does is read from a file, outputs the contents to screen, and then writes them to a different file. It...
6
by: Juha Nieminen | last post by:
joseph cook wrote: Not always. By default, yes, but you can specify other comparators, eg: std::map<int, int, std::greaterreversedMap;
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.