5

I have a very simple test program that uses istringstreams to read in integers from a std::string. The code is:

std::map<int, int> imap;
int idx, value;
std::string str("1 2 3 4 5 6 7 8");
istringstream is(str);
while(is >> idx >> imap[idx]){
    cout << idx << " " << imap[idx] << endl;
}
cout << endl;

std::map<int, int>::iterator itr;
for(itr = imap.begin(); itr != imap.end(); itr++){
    cout << itr->first << " " << itr->second << endl;
}

When I run this on Solaris 10, it produces the following output:

1 2
3 4
5 6
7 8

1 2
3 4
5 6
7 8

However, when I run it under CentOS 7, I get:

1 0
3 0
5 0
7 0

1 4
3 6
5 8
7 0
4204240 2

Does anyone know why it would be different under Linux than under Solaris? It's obviously reading in the value into the map before reading into the index for the map, but I don't know why. I can make it work under Linux by changing the code slightly:

std::map<int, int> imap;
int idx, value;
std::string str("1 2 3 4 5 6 7 8");
istringstream is(str);
while(is >> idx >> value){
    imap[idx] = value;
    cout << idx << " " << imap[idx] << endl;
}

std::map<int, int>::iterator itr;
for(itr = imap.begin(); itr != imap.end(); itr++){
    cout << itr->first << " " << itr->second << endl;
}

I know it's a valid fix, but I have people around me who want to know why it is different. We are migrating from Solaris to Linux and when things like this come up, they want to know why. I don't know why so I'm asking for guidance.

  • The fact that it works on one machine and not the other means it is undefined behavior – smac89 Jan 14 '15 at 21:43
  • 3
    Like similar order-of-operations gotchas posted here, the expression `is >> idx >> imap[idx]` evaluates a variable multiple times while modifying it at least once. See http://stackoverflow.com/questions/4176328 – Drew Dormann Jan 14 '15 at 21:46

1 Answers1

5
is >> idx >> imap[idx]

This expression is equivalent to

operator>>(operator>>(is, idx), imap.operator[](idx))

The evaluations of arguments to the same function are unsequenced relative to each other; either operator>>(is, idx) or imap.operator[](idx) may be evaluated first (that is, either is >> idx or imap[idx] may be evaluated first). If the latter is evaluated first, then the result is an lvalue referring to the value corresponding to the old value of idx in the map; it is this value that will be overwritten by the second read, and not the value corresponding to the new value of idx.

The modified code fixes this by ensuring that idx is read before imap[idx] is accessed.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312