奇怪的cout行为

时间:2016-04-29 14:16:17

标签: c++ string

当使用std::cout向控制台输出内容时,我注意到了一种相当奇怪的行为 我编写了两个函数string& toUpper(std::string &str)string& toLower(std::string &str),它们应该完全按照它们的名称进行操作:将字符串全部转换为大写或全部小写。

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

现在我独立测试了这两个功能,它们按预期工作。然后我在cout电话中链接了他们两个:

string str = "Hello World";
cout << toLower(str) << endl << toUpper(str) << endl;

我预计输出为

hello world
HELLO WORLD

但我只是得到了

hello world
hello world

我使用printf进行了同样的测试,因为我认为这可能是cout做某事的方式所特有但我得到了相同的结果,所以我猜我的代码出了问题。
我的代码有什么问题?

4 个答案:

答案 0 :(得分:4)

您正在修改表达式求值中的变量(字符串),并且依赖于在评估期间的某些点使用它。正如您所发现的那样,您无法依赖它。

一种解决方案是使用不同的字符串;另一个是打破表达:

cout << toLower(str) << endl;
cout << toUpper(str) << endl;

答案 1 :(得分:4)

它是c ++解析你的陈述的方式:

cout << toLower(str) << endl << toUpper(str) << endl; //str = Hello World

第一步评估到上一个:

cout << toLower(str) << endl << str << endl;//str = HELLO WORLD

第二步评估为下:

cout << str << endl << str << endl;//str = hello world

第3步评估cout:

cout <<"hello world\nhello world\n";

你的cout产生这个结果的原因是因为它在打印之前修改了相同的字符串。使用副本而不是引用来解决此问题。

答案 2 :(得分:4)

operator<<的调用必须按顺序从左到右进行,但C ++标准没有指定语句中子表达式的评估顺序。

编译器可以决定评估子表达式的顺序,以便任何这些结果都有效:

auto&& arg1 = toLower(str);
auto&& arg2 = toUpper(str);
cout << arg1 << endl << arg2 << endl;

或者:

auto&& arg1 = toUpper(str);
auto&& arg2 = toLower(str);
cout << arg2 << endl << arg1 << endl;

或者:

auto&& arg1 = toUpper(str);
auto&& arg2 = (cout << arg1);
auto&& arg3 = toUpper(str);
arg2 << endl << arg3 << endl;

或其他几种可能性。在这三种可能的顺序中,只有最后一种顺序会产生您期望的结果。第一个案例将导致&#34; HELLO WORLD&#34;打印两次,第二种情况是编译器得到的结果。根据C ++标准,所有这些都是有效的结果。

答案 3 :(得分:0)

问题是&#34;当它调用函数&#34;以及如何处理引用。由于它是一个缓冲流,它似乎可能无序地调用它们。如果删除返回的字符串的引用(因此,您为每个函数返回一个新的唯一字符串),代码将正常工作。

void toLower(string &str)
{
    for(char &c : str)
        c = tolower(c);

    return str;
}

void toUpper(string &str)
{
    for(auto &c : str)
        c = toupper(c);
}