Click to See Complete Forum and Search --> : Understanding the map template


pedantic
October 10th, 2002, 08:30 AM
I am attempting to understand how to use the MAP (caps for emphasis) standard template. I have figured out how to create a multimap, and how to add items to it, but I am having difficulty trying to reread the multimap. The map declaration looks like this:

#include <map>
#include <string>
#include <iostream>

// Using a string object for both the key
// and the item
typedef multimap<string, string> NameList;

typedef NameList::iterator NameIt;

NameList Names;
NameIt CurrentName;


// An example of adding one element
// to the map:
string NameA1 = "Patty Anderson";
string NameA2 = "Anderson";
Names.insert(NameList::value_type(NameA2, NameA1));

// This is the code I am trying to use to
// output the list to the console:

for(CurrentName = Names.begin(); CurrentName != Names.end(); CurrentName++);
{
// Program crashes here.
cout << (*CurrentName).second << endl;

}

When I get to the cout line of code, the program blows up, even though I am practically copying the line from the VC++ example in their help files.

Can anyone tell me what they think I am doing wrong?

TIA.

proxima centaur
October 10th, 2002, 09:02 AM
I don't know what's wrong with your code it crashes when I run it here too, but this way of parsing the tree works:


CurrentName = Names.begin();
while( CurrentName != Names.end() )
{
cout << (*CurrentName).second;
cout << endl;
++CurrentName;
}


Mystifying.

Edit:

Actually, this also works. I don't know what is wrong with the original code, but here's my code that works:


#include <map>
#include <string>
#include <iostream>

using namespace std;

// Using a string object for both the key
// and the item

typedef multimap<string, string> NameList;

typedef NameList::iterator NameIt;

int main()
{
NameList Names;
NameIt CurrentName;

// An example of adding one element
// to the map:
string NameA1 = "Patty Anderson";
string NameA2 = "Anderson";
Names.insert
(NameList::value_type(NameA2, NameA1));

CurrentName = Names.begin();

while( CurrentName != Names.end() )
{
cout << (*CurrentName).second;
cout << endl;
++CurrentName;
}

for( CurrentName = Names.begin(); CurrentName != Names.end(); CurrentName++ )
{
cout << (*CurrentName).second;
cout << endl;
}
}

pedantic
October 10th, 2002, 09:40 AM
Thanks, Centaur. The while loop worked, but strangely enough, the for loop did not. Go figure.

proxima centaur
October 10th, 2002, 10:07 AM
No problem.

But it really mystifies me as to WHY the for loop sometimes works and sometimes doesn't.

Anybody can help us on that topic?

:)

willchop
October 10th, 2002, 10:19 AM
Why is there a semi-colon after the for loop in the OP's example?
Maybe the compiler sees the loop is a no-op and is optimizing the
loop out of the code completely. This would cause the iterator
to never get initialized.

regards, willchop

pedantic
October 10th, 2002, 10:25 AM
Geez...felled by the stupidest newbie mistake on the books. Yeah, that was the problem. Remove the semi colon and the For loop worked like a charm. Thanks everyone for your help.

proxima centaur
October 10th, 2002, 11:21 AM
Ah!!!!!!!!!!!!!!! thanks willchop !

I didn't see the friggin' semi-colon! That's why it works on my version of the for loop! :D

Don't worry pedantic, we were both as blind as mice! hehehe

Now I understand what happens. You parse the whole multimap and after the loop, your iterator points to Names.end() which is NOT the last element of the map, but rather a flag to tell the iterator when to stop. When you try to access the data pointed by Names.end(), it is an illegal operation punishable by crash.

Ok... I feel better now. I thought the compiler was misbehaving.

pedantic
October 10th, 2002, 11:27 AM
Though I'll add my thanks to willchop, I have to say that I don't feel particularly better about it...(grin).

RNEELY
October 11th, 2002, 11:46 AM
Please help STL Gurus. It seemed to me that one could overload the copy function template to copy multimap value seconds to an ostream iterator. But unfortunately, the following doesn't compile. I get the following error for the copy() call below:
c:\program files\microsoft visual studio\vc98\include\xutility(19) : error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::alloc

Is it possible to do what I am attempting?

Thanks in advance.
Regards,
-Ron


//////////////////////////////////////////////////////////
// multimapfun.cpp
#include <iostream>
#include <string>
#include <map>
#include <iterator>

using namespace std;

typedef multimap<string, string> NameList;

typedef NameList::iterator NameIt;

template <NameIt, class OutputIterator>
OutputIterator copy(NameIt first, NameIt last, OutputIterator result)
{
while(first!=last)
{
*result++ = (*first++).second();
}
return result;
}

int main(int argc, char* argv[])
{
NameList Names;
NameIt CurrentName;

// An example of adding one element
// to the map:
string NameA1 = "Patty Anderson";
string NameA2 = "Anderson";
Names.insert
(NameList::value_type(NameA2, NameA1));

ostream_iterator<string> out(cout, "\n");
copy(Names.begin(), Names.end(), out); // <- error here
cout << endl << flush;

return 0;
}
//
//////////////////////////////////////////////////////////

RNEELY
October 11th, 2002, 11:51 AM
ooops. the whole error message is as follows

c:\program files\microsoft visual studio\vc98\include\xutility(19) : error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::alloc
ator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >' (or there is no acceptable conversion)
c:\working\utility\roncreations\cpp\stl\multimapfun\multimapfun.cpp(42) : see reference to function template instantiation 'class std::ostream_iterator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,c
har,struct std::char_traits<char> > __cdecl std::copy(class std::_Tree<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::all
ocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct std::multimap<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char
,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,cl
*** std::allocator<char> > > >::_Kfn,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char
> > > >::iterator,class std::_Tree<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::bas
ic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct std::multimap<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::_Kfn
,struct std::less<class std::basic_string<char,struct st
::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::iterator,class std::ostream_iterator<class std::basic_string<char,struct std::char_t
raits<char>,class std::allocator<char> >,char,struct std::char_traits<char> >)' being compiled

Paul McKenzie
October 11th, 2002, 12:50 PM
Well one potential error is that you may have two versions of "copy" going that are ambiguous. You have the std::copy, and your own template copy function. I would either

a) call it something else besides copy, or

b) put the scope resolution ::copy() when you call it, or

c) put your version of copy in a namespace and prepend your call to copy with the namespace.

This won't solve your current error, but it is an error that some compilers would have let you know about since the STL headers may include <algorithm> which will include std::copy.

Regards,

Paul McKenzie

Paul McKenzie
October 11th, 2002, 01:00 PM
Also, another error is here:

// *result++ = (*first++).second(); // Error!!
*result++ = (*first++).second; // Correct

The "second" is not a function call, so remove the parentheses.

Regards,

Paul McKenzie

RNEELY
December 3rd, 2002, 09:54 AM
Paul,
Thanks for your response. I cleaned up the code with the suggestions, but I still get the error described below.
I can't figure this one out. Does anyone have any ideas?
Regards,
-Ron

<code>
//////////////////////////////////////////////////////////
// multimapfun.cpp
#pragma warning( disable : 4786) // MSVC++ truncates debug information to '255'
#include <conio.h>
#include <iostream>
#include <string>
#include <map>

#include <iterator>

using namespace std;

typedef multimap<string, string> NameMMap;
typedef NameMMap::iterator NameMMapIt;

template <NameMMapIt, class OutputIterator>
void cpy(NameMMapIt first, NameMMapIt last, OutputIterator result)
{
while(first!=last)
{
*result++ = (*first++).second;
}
return result;
}

int main(int argc, char* argv[])
{
cout << "Welcome to MultiMap Fun!" << endl << flush;

NameMMap Names;
NameMMapIt CurrentName;

// An example of adding one element
// to the map:
string NameA1 = "Patty Anderson";
string NameA2 = "Anderson";
Names.insert(NameMMap::value_type(NameA2, NameA1));

ostream_iterator<string> out(cout, "\n");
cpy(Names.begin(), Names.end(), out); // <- error C2783 here
// error C2783: could not deduce template argument for '__formal'
// A template argument cannot be deduced by the compiler. For example:
// template<class T1, class T2> T1 f(T2){ }
// f(1);
cout << endl << flush;

cout << "Press any key to end..." << endl << flush;
_getch();
return 0;
}
</code>

PaulWendt
December 3rd, 2002, 11:31 AM
Try the following code changes to your cpy definition:

template <typename NameMMapIt, typename OutputIterator>
OutputIterator cpy(NameMMapIt first, NameMMapIt last, OutputIterator result)
{
while (first!=last)
{
*result++ = (*first++).second;
}
return result;
}


After examining some old points, though, I can't figure out why
you're not just using std::copy() [granted, I didn't peruse the
thread TOO intensely].

Edit: I think you just want to print out the map's values; I think
you could use for_each and a functor instead of using your own
version of copy. To each his own :)

--Paul

RNEELY
December 4th, 2002, 11:01 AM
Thanks Paul.

Yes, I am learning STL and I appreciate your help. I rewrote std::copy() as a learning exercise. for_each sounds cool.
It was "typename" that made the difference here:

template <typename NameMMapIt, typename OutputIterator>

I didn't know such a keyword existed. But I should have known the following which works as well:
template <class NameMMapIt, class OutputIterator>

A good understanding of templates seems predicate to understanding STL. I guess when you are defining a template class or function you must always use typename or class for template parameters. And that it is when you are instantiating, or the compiler automatically instantiates, the template the argument types are passed in to the template. I mixed both ways for my template arguments in my template definition.

Regards,
-Ron

PaulWendt
December 4th, 2002, 12:12 PM
Oh alright, I gotcha. I think when I was reading about copy, I
saw a "mock-up" implementation of it so that squelched my desire
to do my own; trust me, I like to know what's going on behind
the scenes too [just not when I don't have to].

--Paul

coldsnake
December 5th, 2002, 01:25 AM
original code:

for(CurrentName = Names.begin(); CurrentName != Names.end(); ++CurrentName);//care this, should not has a semicolon
{
cout << (*CurrentName).second << endl;

}