简体   繁体   中英

Weird cout behaviour

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?

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;

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.

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.

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM