2016-04-29 5 views
3

Я заметил довольно странное поведение при использовании std::cout для вывода чего-то на консоль.
Я написал две функции: string& toUpper(std::string &str) и string& toLower(std::string &str), которые должны делать то, что они вызывают: конвертировать строку в верхнем и нижнем регистре.Weird cout поведение

#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, потому что Я думал, что это может быть что-то особенное для того, чтобы делать вещи, но я получил тот же результат, поэтому, я думаю, у меня что-то не так с моим кодом.
В чем проблема с моим кодом?

+2

Верните копию вместо ссылки, и она будет работать – OMGtechy

+0

Я знаю, что копирование будет работать, но я хочу управлять фактической строкой на месте. – TorbenJ

+1

Ну, вы дважды управляете одной строкой. Cout буферизуется, поэтому я предполагаю, что это вызывает проблему. –

ответ

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; 

Или несколько других возможностей. Из этих трех возможных секвенций только последний даст результат, которого вы ожидаете. Первый случай приведет к тому, что «HELLO WORLD» будет напечатан дважды, а второй случай - результат, который вы получите с вашим компилятором. Все действительные результаты соответствуют стандарту C++.

+0

Спасибо за подробное объяснение и некоторую справочную информацию. Я ожидал, что третье решение произойдет. Жаль, что для этой ситуации не определено определенное поведение. – TorbenJ

0

Проблема: «когда она вызывает функцию» и как обрабатывается эта ссылка. Поскольку это буферизованный поток, он, кажется, вызывает их, возможно, не в порядке. Если вы удалите ссылку для возвращаемой строки (поэтому вы возвращаете новую уникальную строку для каждой функции), код работает правильно.

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

    return str; 
} 

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

минус 1 - вы возвращаете копию после изменения строки. Это не имеет смысла. – Ven

+2

Это не имеет никакого отношения к буферизации вообще. Вызовы функции, которые изменяют строку, оцениваются перед записью в поток. –

4

Вы модификация переменных (строки) в пределы вычисления выражения, и полагаться на него используются в определенные моменты в ходе этой оценки. Как вы нашли, вы не можете полагаться на это.

Одним из решений было бы использование разных строк; другой бы, чтобы разбить выражение:

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

не только вы не можете * полагаться * на это, это просто * неопределенное поведение *. точно так же, как 'i = i ++'. – Ven

+0

@Ven: Нельзя ли полагаться на конкретное поведение, что значит быть неопределенным? –

+1

nope. порядок оценки не определен - в 'a (b(), c())', вы не можете полагаться на 'b()', выполняющийся до 'c()', но там нет неопределенного поведения. Также может быть определено поведение, определяемое им. – Ven

4

Ее путь C++ разбирает ваше заявление:

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

первый шаг оценки TOUPPER:

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

второй этап оценки TOLOWER:

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

3-й шаг оценить соиЬ:

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

Причина ваш соиЬ производит этот результат, потому что это один и тот же необходимости модифицировать строку перед печатью. Используйте эту копию вместо ссылки, чтобы исправить это.