ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

C++: ассоциативность VS порядок вычисления

Не знаю, может, для кого-то это как дважды два, а для меня — открытие.

Ситуация

Написал класс queue, реализующий очередь. У этого класса есть методы put (поместить элемент в очередь) и get (получить элемент из очереди).

Тестирую класс:
queue q1;

q1.put(1); q1.put(2); q1.put(3);
cout << q1.get() << ' ' << q1.get() << ' ' << q1.get() << endl; // вывод 1

q1.put(1); q1.put(2); q1.put(3);
cout << q1.get() << ' ';  // вывод 2
cout << q1.get() << ' ';
cout << q1.get() << endl;

Вывод на экран (операционная система «Windows 7», среда разработки «Visual Studio Community 2017»):
3 2 1
1 2 3

В чём дело

Кто не помнит, очередь работает по принципу FIFO («первым пришёл — первым ушёл»), то есть программа отработала так, как требуется, только во втором случае. Проблема — в выражении, которое я в тексте программы выше пометил комментарием // вывод 1:
cout << q1.get() << ' ' << q1.get() << ' ' << q1.get() << endl;

У операций в языках программирования есть свойство «ассоциативность», которое регулирует последовательность выполнения этих операций при отсутствии явных указаний на очередность при равном приоритете. Для операции вставки << свойство ассоциативности регулирует последовательность выполнения этих операций слева направо, это стандарт C++.

И в среде «Visual Studio Community 2017» этот стандарт, естественно, соблюдается. Например, если бы вместо вызовов методов класса в этом выражении были бы фиксированные значения (литералы), то вывод литералов был бы выполнен, как и положено, в порядке «слева направо»:
cout << 1 << ' ' << 2 << ' ' << 3 << endl;

Вывод на экран:
1 2 3

Ассоциативность-то работает по стандарту, слева направо. Однако, вычисление вызываемых функций происходит не слева направо, а, наоборот, справа налево (для среды «Visual Studio Community 2017»). То есть сначала происходит вычисление функций справа налево и получается выражение:
cout << 3 << ' ' << 2 << ' ' << 1 << endl;

Затем происходит вывод согласно ассоциативности операции вставки, то есть слева направо:
3 2 1

Как оказалось, в стандарте C++ порядок вычисления функций в такой ситуации не определен и отдан на откуп конкретным реализациям компилятора. Источник:
https://en.cppreference.com/w/cpp/language/eval_order

Что делать?

В интернетах рекомендуют просто составлять такие выражения, которые не будут содержать таких неопределенностей. То есть, к примеру, так, как сделано в тексте программы выше, там, где помечено комментарием // вывод 2.

Обсуждения на портале Stackoverflow.com:
https://stackoverflow.com/questions/11354871/c-input-and-output-stream-operators-associativity
https://stackoverflow.com/questions/38870556/when-does-operator-refer-to-the-insertion-operator-and-when-to-the-bitwise-lef
Tags: Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments