What is wrong with reference into std::map?

J

Jim Langston

Expected output of program:
Key is: 0 String is: Hello
Key is: 1 String is: Goodbye
Key is: 2 String is: The end

Actual output:
Key is: 0 String is: The End
Key is: 1 String is:
Key is: 2 String is:

What am I doing wrong?

If I declare my reference fresh each time, such as putting { } around the
places I insert and doing
std::string& MyString = (*it).second;
each time it will come out right.

Why can't I reuse the references?

It took a long time to find out what was causing this in my program. This
is just a test program showing the issue.

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

std::map<unsigned int, std::string> MyMap;

int main ()
{
unsigned int ID = 0;
std::map< unsigned int, std::string>::iterator it = MyMap.insert(
MyMap.end(), std::make_pair< unsigned int, std::string >( ID,
std::string() ) );
std::string& MyString = (*it).second;
MyString = "Hello";

ID = 1;
it = MyMap.insert( MyMap.end(), std::make_pair< unsigned int,
std::string >( ID, std::string() ) );
std::string& MyString2 = (*it).second;
MyString = "Goodbye";

ID = 2;
it = MyMap.insert( MyMap.end(), std::make_pair< unsigned int,
std::string >( ID, std::string() ) );
MyString = (*it).second;
MyString = "The End";

for ( std::map< unsigned int, std::string >::iterator i = MyMap.begin();
i != MyMap.end(); ++i )
{
std::cout << "Key is: " << (*i).first << " String is: " <<
(*i).second << std::endl;
}

std::string wait;
std::cin >> wait;
}
 
J

Jim Langston

Jim Langston said:
Expected output of program:
Key is: 0 String is: Hello
Key is: 1 String is: Goodbye
Key is: 2 String is: The end

Actual output:
Key is: 0 String is: The End
Key is: 1 String is:
Key is: 2 String is:

What am I doing wrong?

If I declare my reference fresh each time, such as putting { } around the
places I insert and doing
std::string& MyString = (*it).second;
each time it will come out right.

Why can't I reuse the references?

It took a long time to find out what was causing this in my program. This
is just a test program showing the issue.

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

std::map<unsigned int, std::string> MyMap;

int main ()
{
unsigned int ID = 0;
std::map< unsigned int, std::string>::iterator it = MyMap.insert(
MyMap.end(), std::make_pair< unsigned int, std::string >( ID,
std::string() ) );
std::string& MyString = (*it).second;
MyString = "Hello";

ID = 1;
it = MyMap.insert( MyMap.end(), std::make_pair< unsigned int,
std::string >( ID, std::string() ) );
std::string& MyString2 = (*it).second;

This was a broken attempt to diagnose. Even with this line changed to:
std::string& MyString2 = (*it).second

the output is the same.
 
R

Ron Natalie

Jim said:
std::string& MyString2 = (*it).second;
MyString = "Goodbye";

You're going to kick yourself, but you want MyString2 in
the above line. You're righting into the first inserted
string here.
ID = 2;
it = MyMap.insert( MyMap.end(), std::make_pair< unsigned int,
std::string >( ID, std::string() ) );
MyString = (*it).second;

This doesn't initialize a reference, it assigns the value of second
into where MyString was initialized (your first insertion).
MyString = "The End";

And this does another such assignment
 
J

Jim Langston

Ron Natalie said:
You're going to kick yourself, but you want MyString2 in
the above line. You're righting into the first inserted
string here.

This doesn't initialize a reference, it assigns the value of second
into where MyString was initialized (your first insertion).

Oh, well, shoot. You're right. That totally excaped me. If the reference
is declared on the same line, then it initializes it.

Hmm.. so how do you assign a reference that has already been initialized?
I.E.
int int1;
int int2;
int& MyRef = int1;
Who do I do to get MyRef to point to int2 now? Or can I?
 
P

Phlip

Could you break up all these long lines, pleeeeze? Start with

typedef std::make_pair< unsigned int, std::string > mapType;

then use that everywhere you copied in that whole gizmo.
Hmm.. so how do you assign a reference that has already been initialized?
int& MyRef = int1;
Who do I do to get MyRef to point to int2 now? Or can I?

You can't reseat a reference. That's a feature. You said "point to", which
betrays you think of a reference as a kind of pointer. It's really another
name for something; an alias.

Will a map let you use iterators? Some containers invalidate their iterators
after an insert, but I can't think of a technical reason for a binary tree
to do that, and maps generally implement as binary trees.
 
J

Jim Langston

Phlip said:
Could you break up all these long lines, pleeeeze? Start with

typedef std::make_pair< unsigned int, std::string > mapType;

then use that everywhere you copied in that whole gizmo.

Yeah, well, the problem is I'm using a number of maps, 4, and I would have
to do that for every map since they're different pair types. By the time I
typedefed the maps, the iterators and the make pairs that's 12 different
things I have to remember what is what.

It's just easier for me to type them in so I don't have to keep going back
and seeing what they are.
You can't reseat a reference. That's a feature. You said "point to", which
betrays you think of a reference as a kind of pointer. It's really another
name for something; an alias.

Will a map let you use iterators? Some containers invalidate their
iterators after an insert, but I can't think of a technical reason for a
binary tree to do that, and maps generally implement as binary trees.

Well, I'm only using the reference after the insert to set the data, then I
don't use it anymore. The reference may be invalidated after another
insert, I'm not sure.

Well, I came across this problem and figured out that by putting brackets
around the whole thing the problem went away, I just wanted to know what the
problem was before I converted it into a function.

I'm goign to have to remember that you can't reset a reference. Dang, I
hope they add that to the language. Even if it takes another keyword.

std::reseat MyRef = int2;
MyRef<std::reseat> = int2;
std::reseat<int&>( MyRef ) = int2;

any format
 
A

Abdo Haji-Ali

Jim said:
Well, I came across this problem and figured out that by putting brackets
around the whole thing the problem went away, I just wanted to know what the
problem was before I converted it into a function.
You're not reseting the reference in this case, you're just creating a
new one with new initialization
I'm goign to have to remember that you can't reset a reference. Dang, I
hope they add that to the language. Even if it takes another keyword.
The language already has this, it's called a pointer :)
std::reseat MyRef = int2;
MyRef<std::reseat> = int2;
std::reseat<int&>( MyRef ) = int2;

int *pPointer = &int2;
*pPointer = 5;
pPointer = int3; // Reseting the pointer

Abdo Haji-Ali
Programmer
In|Framez
 
J

Jim Langston

Abdo Haji-Ali said:
You're not reseting the reference in this case, you're just creating a
new one with new initialization

The language already has this, it's called a pointer :)


int *pPointer = &int2;
*pPointer = 5;
pPointer = int3; // Reseting the pointer

Well, yes, I know how to reset pointers. I've been using pointers from my C
days. But I now prefer references.
 
P

Phlip

Jim said:
Yeah, well, the problem is I'm using a number of maps, 4, and I would have
to do that for every map since they're different pair types. By the time
I typedefed the maps, the iterators and the make pairs that's 12 different
things I have to remember what is what.

That sounds like an argument _for_ typedeffing.
It's just easier for me to type them in so I don't have to keep going back
and seeing what they are.

Give them stupid names, like map_int_string. Nobody said the names had to
reveal their logical intent.

(Actually, someone _did_ say that. Skip that rule for now!)
I'm goign to have to remember that you can't reset a reference. Dang, I
hope they add that to the language. Even if it takes another keyword.

std::reseat MyRef = int2;
MyRef<std::reseat> = int2;
std::reseat<int&>( MyRef ) = int2;

What's the point? Just use a pointer if you need the ability to re-seat.

That's why you should prefer references to pointers unless you need
pointers' extra abilities. Adding such abilities to pointers would dilute
their effectiveness as the _weakest_ kind of handle we have.
 
J

Jakob Bieling

Jim Langston said:
Well, yes, I know how to reset pointers. I've been using pointers
from my C days. But I now prefer references.

Better use pointers when they are appropriate and do not
dogmatically
stick to references. If you want to reseat, then pointers _are_
appropriate.

If you need reseating functionality here is a different matter. I
have
not looked closely at the code to advise on that.

(sorry I accidently replied to your email at first)

regards
 
R

Richard Herring

Phlip said:
Will a map let you use iterators? Some containers invalidate their iterators
after an insert, but I can't think of a technical reason for a binary tree
to do that, and maps generally implement as binary trees.
True, but irrelevant since it's not guaranteed by the standard. The
following is:

23.1.2/8. The insert members shall not affect the validity of iterators
and references to the [associative] container, and the erase members
shall invalidate only iterators and references to the erased elements.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top