Reputation: 4582
I noticed a rather strange behaviour when using std::cout
for outputting something to the console.
I wrote two functions string& toUpper(std::string &str)
and string& toLower(std::string &str)
which should do exactly what they are called after: Convert a string all uppercase or all lowercase.
#include <string>
using namespace std;
string& toLower(string &str)
{
for(char &c : str)
c = tolower(c);
return str;
}
string& toUpper(string &str)
{
for(auto &c : str)
c = toupper(c);
return str;
}
Now I tested both functions independently and they are working as expected. Then I chained both of them in a cout
call:
string str = "Hello World";
cout << toLower(str) << endl << toUpper(str) << endl;
I expected the output to be
hello world
HELLO WORLD
but instead I just got
hello world
hello world
I tested the same using printf
because I thought it might be something specific to cout
's way of doing stuff but I got the same result, so I guess I got something wrong with my code.
What's the problem with my code?
Upvotes: 3
Views: 162
Reputation: 1473
The issue is "when it is calling the function" and how the reference is being processed. As it is a buffered stream, it seems to be calling them possibly out of order. If you remove the reference for the returned string (So you are returning a new unique string for each function) the code works properly.
void toLower(string &str)
{
for(char &c : str)
c = tolower(c);
return str;
}
void toUpper(string &str)
{
for(auto &c : str)
c = toupper(c);
}
Upvotes: 0
Reputation: 171263
The calls to operator<<
must happen in order, left-to-right, but the C++ standard does not specify the order of evaluation of sub-expressions within the statement.
The compiler can decide in which order to evaluate the sub-expressions, so that any of these outcomes is valid:
auto&& arg1 = toLower(str);
auto&& arg2 = toUpper(str);
cout << arg1 << endl << arg2 << endl;
Or:
auto&& arg1 = toUpper(str);
auto&& arg2 = toLower(str);
cout << arg2 << endl << arg1 << endl;
Or:
auto&& arg1 = toUpper(str);
auto&& arg2 = (cout << arg1);
auto&& arg3 = toUpper(str);
arg2 << endl << arg3 << endl;
Or several other possibilities. Of these three possible sequencings, only the last one would produce the result you expect. The first case would result in "HELLO WORLD" being printed twice, and the second case is the result you get with your compiler. All are valid outcomes according to the C++ standard.
Upvotes: 4
Reputation: 505
Its the way c++ parses your statement:
cout << toLower(str) << endl << toUpper(str) << endl; //str = Hello World
1st step evaluate toUpper:
cout << toLower(str) << endl << str << endl;//str = HELLO WORLD
2nd step evaluate toLower:
cout << str << endl << str << endl;//str = hello world
3rd step evaluate cout:
cout <<"hello world\nhello world\n";
The reason your cout produces this result is because it is modifing the same string before printing. Use a copy instead of a reference to fix this.
Upvotes: 4
Reputation: 49803
You are modifying a variable (the string) within the evaluation of an expression, and are relying on it being used at certain points during that evaluation. As you have found, you can't rely on that.
One solution would be to use different strings; another would be to break up the expression:
cout << toLower(str) << endl;
cout << toUpper(str) << endl;
Upvotes: 4