A comment to this reply states that the following code should not be used because it exhibits undefined behavior:
int old = (std::cin >> old, old);
A similar code was also greatly scorned here, in particular for exhibiting undefined behavior.
On the other hand, this highly up-voted reply suggests the following code as an example of usefulness of the comma operator:
while (cin >> str, str != "STOP") {
//process str
}
I assume that this code would not be up-voted if it exhibited undefined behavior.
The question: if the first code is undefined behavior (presumably because of using the result of reading from cin
without checking the status of the latter), then why is the second code fine?
EDIT: The question is answered in the comment section for the first example. What the second example does not show is that str
is an instance of std::string
and so is initialized. Therefore, there is no undefined behavior.
The statement int old = (std::cin >> old, old);
is well-defined since C++11 assuming that non-whitespace characters are read. This is because old
is set to zero if std::cin
fails in such a case. The use of the expression separator operator ,
is also legitimate as it sequences the expressions std::cin >> old
and old
. If no non-whitespace characters are countered then old
is still not changed by std::cin >> old
and the behaviour of the code is undefined.
Assuming str
is a std::string
type, (cin >> str, str != "STOP")
is always
well-defined. If cin >> str
fails then initial (perhaps default-constructed) value of str
is retained, and again ,
sequences the expressions.
Both answers are bad answers but only first one may exhibit undefined behavior.
int old = (std::cin >> old, old);
When std::cin >> old
fails then old
can have its value that it had. So it can remain as uninitialized int
and so reading from it is undefined behavior.
while (cin >> str, str != "STOP") { //process str }
When cin >> str
fails then str
has its previous value. Assuming str
is std::string
it can not have had uninitialized state unlike aggregate types. So it will have its previous value for example default-constructed value ""
or previously read value "MOP"
. Reading from it and comparing with "STOP"
is never undefined behavior. However it is very likely that cin >> str
will fail next cycle again and so str will never be "STOP"
and so it will be endless loop and so &&
is likely better than comma there.
Also it is worth to note that we can get real crash when we try to combine first example and second example:
std::string old = (std::cin >> old, old);
When streams are not set to throw on failures then we should check state of those manually (and std::cin
is not special) since those may end and fail. Unfortunately lot of tutorial examples do not do it. Therefore some people seem to think that the stream operations are suitable for inserting into middle of some expression using comma operator.
Edit:
I am unsure about comment that it is different from C++11. Did dig, but couldn't find confirmation in standard. Trying this code with g++ did not read zero on case of error:
#include <iostream>
#include <sstream>
int main()
{
std::stringstream in("42");
int i = 0;
std::cout << i << '\n'; // outputs 0
in >> i;
std::cout << i << '\n'; // outputs 42
i = 666;
in >> i;
std::cout << i << '\n'; // outputs 666
// so it did leave value of i like it was
}
Demo. Also std::cin does not seem special in any way. Of course it may be that the implementation of g++ is incorrect or that the failed input is required to set the i
only on case of subset of failures and so the first example still may exhibit undefined behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With