Почему 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

Что я делаю неправильно?

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

Ответ 1

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

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

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

Ответ 2

Проблема заключается не в ассоциативности в этом выражении:

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

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

Ответ 3

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

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