Почему оператор конкатенации std::string работает как ассоциативный справа?

Запуск следующего MWE, извлеченного из моего любимого проекта и скомпилированного с GCC 4.9.1 (и также 4.8.1)

#include <iostream>
#include <string>
#include <sstream>

class InputStringStream
{
public:
    InputStringStream(const std::string& str) : istringstream(str), currentLine() {}
    std::string readLine()
    {
        std::getline(istringstream, currentLine);
        return currentLine;
    }

private:
    std::istringstream istringstream;
    std::string currentLine;
};

int main()
{
    std::string s = std::string("line1\nline2\nline3");
    InputStringStream stream(s);
    std::cout << stream.readLine() + "\n" + stream.readLine() + "\n" + stream.readLine() << std::endl;
    return 0;
}

производит следующий вывод

line3
line2
line1

пока я ожидаю

line1
line2
line3

Что я делаю не так?

PS Тот же код, скомпилированный с помощью компилятора Apple LLVM версии 5.1, дает то, что я ожидаю. Visual C++ 2012 на стороне GCC.

3 ответа

Решение

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

Вы должны извлечь элементы потока в детерминированном порядке, и это ваша обязанность. Например:

std::cout << stream.readLine() + '\n';
std::cout << stream.readLine() + '\n';
std::cout << stream.readLine() + '\n';

Еще лучше, избегая избыточности и временных строк:

for (auto i : { 1, 2, 3 }) { std::cout << stream.readLine() << '\n'; }

Проблема не в ассоциативности, в этом выражении:

stream.readLine() + "\n" + stream.readLine() + "\n" + stream.readLine() 

Это не указано, какой stream.readLine() называется первым.

Единственное, что вы делаете неправильно, это предполагая, что в выражении, в котором вы называете stream.readLine() трижды порядок появления этих выражений совпадает с порядком вызовов. Некоторые компиляторы могут сначала оценить последний из вызовов, некоторые могут оценить их по порядку. Теоретически, некоторые могут даже оценить средний в первую очередь. Это всего лишь общее правило C++: порядок вычисления выражения не указан.

Простой способ получить одинаковые результаты во всех реализациях - сохранить эти три результата в отдельных переменных.

Другие вопросы по тегам