f() + g() * h() + j()
• Приоритет гарантирует умножение результатов вызова функций g() и h().
• Порядок гарантирует добавление результата вызова функции f() к произведению g() и h(), а также добавление результата сложения к результату вызова функции j().
• Однако нет никаких гарантий относительно порядка вызова этих функций.
Если функции f(), g(), h() и j() являются независимыми и не влияют на состояние тех же объектов или выполняют ввод и вывод, то порядок их вызова несуществен. Но если любые из этих функций действительно воздействуют на тот же объект, то выражение ошибочно, а его поведение непредсказуемо.
Упражнение 4.3. Порядок вычисления большинства парных операторов оставляется неопределенным, чтобы предоставить компилятору возможность для оптимизации. Эта стратегия является компромиссом между созданием эффективного кода и потенциальными проблемами в использовании языка программистом. Полагаете этот компромисс приемлемым? Кто-то да, кто- то нет.
При написании составных выражений могут пригодиться два эмпирических правила.
1. В сомнительных случаях заключайте выражения в круглые скобки, чтобы явно сгруппировать операнды в соответствии с логикой программы.
2. При изменении значения операнда не используйте этот операнд в другом месте того же оператора.
Важнейшим исключением из второго правила является случай, когда часть выражения, изменяющая операнд, сама является операндом другой части выражения. Например, в выражении *++iter инкремент изменяет значение итератора iter, а измененное значение используется как операнд оператора *. В этом и подобных выражениях порядок обработки операндов не является проблемным. Но в больших выражениях те части, которые изменяют операнд, должны обрабатываться в первую очередь. Такой подход не создает никаких проблем и применяется достаточно часто.
4.2. Арифметические операторы
Таблица 4.1. Арифметические операторы
(левосторонний порядок)
| Оператор | Действие | Применение |
|---|---|---|
+ | Унарный плюс | + |
- | Унарный минус | - |
* | Умножение | |
/ | Деление | |
% | Остаток | |
+ | Сложение | |
- | Вычитание | |
В табл. 4.1 (и таблицах операторов последующих разделов) операторы сгруппированы по приоритету. Унарные арифметические операторы имеют более высокий приоритет, чем операторы умножения и деления, которые в свою очередь имеют более высокий приоритет, чем парные операторы вычитания и сложения. Операторы с более высоким приоритетом группируются перед операторами с более низким приоритетом. Все эти операторы имеют левосторонний порядок, т.е. при равенстве приоритетов они группируются слева направо.
Если не указано иное, то арифметические операторы могут быть применены к любому арифметическому типу (см. раздел 2.1.1) или любому типу, который может быть преобразован в арифметический тип. Операнды и результаты этих операторов являются r-значениями. Как упоминается в разделе 4.11, в ходе вычисления операторов их операнды малых целочисленных типов преобразуются в больший целочисленный тип и все операнды могут быть преобразованы в общий тип.
Унарные операторы плюс и минус могут быть также применены к указателям. Использование парных операторов + и - с указателями рассматривалось в разделе 3.5.3. Будучи примененным к указателю или арифметическому значению, унарный плюс возвращает (возможно, преобразованную) копию значения своего операнда.
Унарный оператор минус возвращает отрицательную копию (возможно, преобразованную) значения своего операнда.
int i = 1024;
int k = -i; //
bool b = true;
bool b2 = -b; //