# Глава 2.2. Операторы
Вы уже читали примеры кода, в которых используется [оператор присваивания](https://en.cppreference.com/w/cpp/language/operator_assignment) `=`, оператор взятия элемента по индексу `[]` и оператор `.` для доступа к члену класса. Рассмотрим ещё несколько категорий операторов, которые пригодятся в первую очередь.
Чтобы поменять порядок выполнения операторов, они группируются скобками:
```cpp
int x = (a + 5) * 8;
```
## Операторы сравнения
[Операторы сравнения](https://en.cppreference.com/w/cpp/language/operator_comparison) (comparison operators) применимы к большинству фундаментальных типов:
- `==` — равенство.
- `!=` — неравенство.
- `<`, `>` — меньше, больше.
- `<=`, `>=` — меньше или равно, больше или равно.
Выражения сравнения приводятся к типу `bool`. **Выражение** (expression) — это последовательность операторов и операндов.
```cpp
bool a = 8.1 < 16; // true
bool b = -5 != -5; // false
std::string s = "";
bool c = s.empty(); // true
bool d = s.size() == 4; // false
```
Чему равно значение `b`? {.task_text}
```cpp {.example_for_playground .example_for_playground_005}
std::string text = "Operator";
bool b = text[text.size() - 1] == text[3];
```
```consoleoutput {.task_source #cpp_chapter_0022_task_0090}
```
`text.size()` вернёт 8. Символ по индексу 7 равен `r`. Символ по индексу 3 тоже равен `r`. {.task_hint}
```cpp {.task_answer}
true
```
## Логические операторы
[Логические операторы](https://en.cppreference.com/w/cpp/language/operator_logical) применимы ко всем выражениям, которые приводятся к `bool`:
- `&&` — «И»: `is_filled && is_valid`.
- `||` — «ИЛИ»: `has_gps_location || connected_to_wifi`.
- `!` — «НЕ» (отрицание): `!is_valid`.
```cpp {.example_for_playground .example_for_playground_001}
bool is_online = true;
bool is_updated = false;
std::println("{}", is_online || is_updated); // true
std::println("{}", !(is_online && is_updated)); // true
```
XOR — это булева функция, также известная как [исключающее «ИЛИ».](https://ru.wikipedia.org/wiki/%D0%98%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B0%D1%8E%D1%89%D0%B5%D0%B5_%C2%AB%D0%B8%D0%BB%D0%B8%C2%BB) Она принимает два флага и возвращает `true`, если один из них истинен, а другой — ложен. В остальных случаях она возвращает `false`. {.task_text}
Напишите свою реализацию `hello_xor()`. {.task_text}
```cpp {.task_source #cpp_chapter_0022_task_0050}
```
Функция возвращает `true` тогда и только тогда, когда один из аргументов равен `true`, а другой — `false`. {.task_hint}
```cpp {.task_answer}
bool hello_xor(bool a, bool b)
{
return (!a && b) || (a && !b);
}
```
## Арифметические операторы
[Арифметические операторы](https://en.cppreference.com/w/cpp/language/operator_arithmetic) (arithmetic operators) применимы к любым выражениям, которые приводятся к числам. Они позволяют осуществлять:
- `+` — сложение: `5 + 6 == 11`.
- `-` — вычитание. `8 - 9 == -1`.
- `*` — умножение. `3 * 7 == 21`.
- `/` — деление. `10 / 4 == 2`.
- `%` — деление по модулю, то есть получение остатка от деления. `11 % 3 == 2`.
Перечисленные операторы называются бинарными. Они применяются к двум операндам: `a + b`. В C++ есть и унарные операторы — унарные плюс и минус: `+a`, `-a`.
## Составное присваивание {#block-compound-assignment}
[Операторы составного присваивания](https://en.cppreference.com/w/cpp/language/operator_assignment) (compound assignment) объединяют присваивание переменной с арифметическим действием над ней. Их ввели в язык, чтобы записывать простые арифметические действия более кратко:
```cpp
x += 5; // x = x + 5
x -= y; // x = x - y
x *= 10; // x = x * 10;
x /= y; // x = x / y;
x %= 2; // x = x % 2;
```
## Возвращаемое значение и побочные эффекты
Операторы удобно рассматривать как функции, а их операнды — как аргументы этих функций. Тогда результат операции — это _возвращаемое значение_ соответствующей «функции».
Упрощённо, _побочный эффект_ оператора — это изменение хотя бы одного из операндов в процессе вычисления оператора.
С арифметическими операторами всё просто: их возвращаемое значение определяется смыслом соответствующего арифметического действия, а побочных эффектов они не имеют. Другое дело — операторы присваивания, в том числе составного. Очевидно, у них есть побочный эффект — это изменение своего левого операнда. Но также они _возвращают_ обновлённое значение своего левого операнда (точнее, ссылку на него, но сейчас это не так важно).
Рассмотрим команду `y = (x += 2);`. Выражение в скобках вернёт изменённое значение переменной `x`, и оно будет присвоено переменной `y`.
## Инкремент и декремент
Увеличение или уменьшение значения на единицу можно записывать ещё короче! [Оператор инкремента](https://en.cppreference.com/w/cpp/language/operator_incdec) `++` увеличивает значение на 1, а оператор декремента `--` уменьшает. Эти операторы применимы _только_ к целым числам.
```cpp
++x; // Эквивалентно x+=1
--x; // Эквивалентно x-=1
```
В этом примере приведена префиксная форма операторов: пре-инкремент и пре-декремент.
Есть и постфиксная форма: в ней `++` и `--` указываются после переменной. Это называется пост-инкрементом и пост-декрементом:
```cpp
x++;
x--;
```
Обе формы имеют один и тот же побочный эффект — они изменяют переменную. Разница в возвращаемом значении.
Префиксный оператор возвращает обновлённое значение переменной: {#block-pre-increment}
```cpp
a = 2;
b = ++a; // a=3, b=3
```
Постфиксный оператор возвращает старое значение переменной:
```cpp
a = 2;
b = a++; // a=3, b=2
```
Постфиксные операторы инкремента и декремента имеют более высокий приоритет, чем префиксные.
И ещё одно важное отличие: префиксные формы возвращают саму переменную, а постфиксные — её неизменяемую копию. Поэтому такой код не скомпилируется:
```cpp
--i++;
```
Вначале выполнится постфиксный оператор и вернёт неизменяемую копию переменной: `--(i++)`. А так как неизменяемое значение нельзя уменьшить, компилятор прервёт сборку программы с ошибкой:
```
error: expression is not assignable
--i++;
^ ~~~
```
## Приоритет и ассоциативность операторов
Перечислим уже знакомые вам операторы по убыванию приоритета. Если в строке несколько операторов, то приоритет у них одинаковый.
- `a::b` — разрешение области видимости, например `std::println()`. Это оператор с наивысшим приоритетом.
- `a++`, `a--`, `a[b]`, `a.b` — постфиксные инкремент и декремент, взятие по индексу, доступ к члену класса.
- `!a`, `+a`, `-a`, `++a`, `--a` — логическое отрицание, унарные плюс и минус, префиксные инкремент и декремент.
- `a * b`, `a / b`, `a % b` — умножение, деление, деление по модулю.
- `a + b`, `a - b` — сложение, вычитание.
- `a < b`, `a <= b`, `a > b`, `a >= b` — больше, меньше.
- `a == b`, `a != b` — равенство, неравенство.
- `a && b` — логическое «И».
- `a || b` — логическое «ИЛИ».
- `a = b`, `a += b`, `a -= b`, `a *= b`, `a /= b`, `a %= b` — присваивание, составное присваивание.
Порядок вычисления можно изменять с помощью скобок.
Если рядом стоят два оператора с равным приоритетом, то порядок вычислений определяется ассоциативностью. Так, поскольку `+` — левоассоциативный оператор, то выражение `a + b + c` будет вычисляться как `(a + b) + c.` Оператор присваивания, наоборот, правоассоциативный, именно поэтому присваивание `a = b = 0` работает ожидаемым образом. Ведь оно эквивалентно записи `a = (b = 0).`
Таблицу с приоритетом и ассоциативностью _всех_ операторов C++ вы можете [посмотреть на cppreference.](https://en.cppreference.com/w/cpp/language/operator_precedence)
Нужны ли скобки, чтобы это выражение вычислилось как ожидается? `y/n`. {.task_text}
```cpp
width < 0 || volume / length <= max_val
```
```consoleoutput {.task_source #cpp_chapter_0022_task_0060}
```
Приоритет деления `/` выше, чем сравнения `<=`. А приоритет `||` меньше, чем сравнения. {.task_hint}
```cpp {.task_answer}
n
```
Как быть, если вы сомневаетесь, нужны ли в выражении скобки? [Если без скобок код трудно читать, то ставьте их!](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-parens) Например, выражение из задачи выше со скобками выглядит проще: `(width < 0) || (volume / length <= max_val)`.
Какое значение у переменной `x`? {.task_text}
В случае ошибки напишите `err`. {.task_text}
Ремарка: это пример плохого кода. В реальных проектах избегайте подобных трудночитаемых конструкций. Однако они встречаются на собеседованиях. {.task_text}
```cpp {.example_for_playground .example_for_playground_002}
int a = 1, b = 2, c = 3;
int x = a-- - b++ - c--;
```
```consoleoutput {.task_source #cpp_chapter_0022_task_0070}
```
Приоритет постфиксных операторов выше, чем у оператора вычитания `-`. Постфиксный оператор сначала возвращает значение переменной, а потом изменяет его. Поэтому `x` равен `1 - 2 - 3`. {.task_hint}
```cpp {.task_answer}
-4
```
Что будет выведено в консоль? {.task_text}
В случае ошибки напишите `err`. {.task_text}
```cpp {.example_for_playground .example_for_playground_003}
int c = 2;
int C = 5;
std::print("{}", c++ * ++C);
```
```consoleoutput {.task_source #cpp_chapter_0022_task_0080}
```
В этом выражении у постфиксного оператора максимальный приоритет. Следующим по приоритету выполнится префиксный оператор. И лишь затем — оператор умножения. Мы получим `2 * 6`. {.task_hint}
```cpp {.task_answer}
12
```
----------
## Резюме
- Операторы сравнения: `==`, `!=`, `<`, `>`, `<=`, `>=`.
- Логические операторы: `&&`, `||`, `!`.
- Арифметические операторы: `+`, `-`, `*`, `/`, `%`.
- Операторы составного присваивания — это краткая форма выполнения над переменной арифметического действия и присваивания ей. Например, `x *= 2`.
- Операторы инкремента `++` и декремента `--` бывают префиксными и постфиксными.
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!