5.5 – Оператор запятая и условный оператор

Добавлено 8 мая 2021 в 11:25

Оператор запятая

Оператор запятая
ОператорОбозначениеПример использованияОперация
Запятая,x, yВычисляет x, затем y, возвращает значение y

Оператор запятая (,) позволяет вычислять несколько выражений везде, где разрешено одно выражение. Оператор запятая вычисляет левый операнд, затем правый операнд, а затем возвращает результат правого операнда.

Например:

#include <iostream>
 
int main()
{
    int x{ 1 };
    int y{ 2 };
 
    std::cout << (++x, ++y); // увеличиваем x и y, 
                             // вычисляется как значение правого операнда
 
    return 0;
}

Сначала вычисляется левый операнд оператора запятой, который увеличивает x с 1 до 2. Затем вычисляется правый операнд, который увеличивает y с 2 до 3. Оператор запятой возвращает результат правого операнда (3), который впоследствии выводится в консоль.

Обратите внимание, что запятая имеет самый низкий приоритет среди всех операторов, даже ниже, чем присваивание. Из-за этого следующие две строки кода делают разные вещи:

// сначала вычисляем (a, b), чтобы получить результат b, 
// затем присваиваем это значение переменной z.
z = (a, b); 

// вычисляется как "(z = a), b", поэтому z присваивается значение a, 
// после чего b вычисляется и отбрасывается.
z = a, b;

Это делает использование оператора запятой несколько опасным.

Практически в каждом случае инструкцию, написанную с использованием оператора запятой, лучше записать в виде отдельных инструкций. Например, приведенный выше код можно записать как:

#include <iostream>
 
int main()
{
    int x{ 1 };
    int y{ 2 };
 
    ++x;
    std::cout << ++y;
 
    return 0;
}

Большинство программистов вообще не используют оператор запятой, за единственным исключением внутри циклов for, где его использование довольно распространено. Мы обсудим циклы for в следующем уроке «7.9 – Инструкции for».

Лучшая практика


Избегайте использования оператора запятой, за исключением циклов for.

Запятая как разделитель

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

// Запятая используется для разделения параметров в определении функции
void foo(int x, int y) 
{
    // Запятая используется для разделения аргументов при вызове функции
    add(x, y); 

    // Запятая используется для разделения нескольких переменных, определенных 
    // в одной строке (не делайте так)
    constexpr int z{ 3 }, w{ 5 }; 
}

Избегать разделительных запятых нет необходимости (кроме случаев объявления нескольких переменных, чего делать не следует).

Условный оператор

Условный оператор
ОператорОбозначениеПример использованияОперация
Условный?:c ? x : yЕсли c не равно нулю (т.е. вычисляется как true), то вычисляется x, в противном случае вычисляется y

Условный оператор (?:) (также иногда называемый оператором «арифметическое если») является тернарным оператором (он принимает 3 операнда). Поскольку исторически это единственный тернарный оператор C++, его также иногда называют «тернарным оператором».

Оператор ?: предоставляет сокращенный метод для выполнения определенного типа инструкции if/else. Если вам нужно освежить в памяти if/else, то прежде чем продолжить просмотрите урок «4.10 – Знакомство с операторами if».

Оператор if/else имеет следующую форму:

if (условие)
    инструкция_1;
else
    инструкция_2;

Если условие истинно, то выполняется инструкция_1, в противном случае выполняется инструкция_2.

Оператор ?: имеет следующую форму:

(условие) ? выражение_1 : выражение_2;

Если условие истинно, то выполняется выражение_1, в противном случае выполняется выражение_2. Обратите внимание, что выражение_2 не является необязательным.

Рассмотрим оператор if/else, который выглядит так:

if (x > y)
    larger = x;
else
    larger = y;

Его можно переписать следующим образом:

larger = (x > y) ? x : y;

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

Заключение условного оператора в скобки

Условная часть оператора обычно помещается в круглые скобки, чтобы облегчить чтение, а также, чтобы убедиться в правильности приоритетов. Остальные операнды вычисляются так, как если бы они были заключены в круглые скобки, поэтому явные скобки для них не требуются.

Обратите внимание, что оператор ?: имеет очень низкий приоритет. Если вы делаете что-либо, кроме присваивания результата переменной, весь оператор ?: также должен быть заключен в круглые скобки.

Например, чтобы вывести на экран большее из значений x и y, мы могли бы сделать это:

if (x > y)
    std::cout << x;
else
    std::cout << y;

Или мы могли бы использовать для этого условный оператор:

std::cout << ((x > y) ? x : y);

Давайте посмотрим, что произойдет, если в приведенном выше случае мы не заключим в скобки весь условный оператор.

Поскольку оператор << имеет более высокий приоритет, чем оператор ?:, следующая инструкция:

std::cout << (x > y) ? x : y;

будет вычисляться как:

(std::cout << (x > y)) ? x : y;

Она напечатает 1 (true), если x > y, или 0 (false) в противном случае!

Лучшая практика


Всегда заключайте в скобки условную часть условного оператора, а также подумайте о том, чтобы заключить его в скобки полностью.

Условный оператор вычисляется как выражение

Поскольку операнды условного оператора являются выражениями, а не операторами, условный оператор может использоваться в некоторых местах, где нельзя использовать if/else.

Например, при инициализации константной переменной:

#include <iostream>
 
int main()
{
    constexpr bool inBigClassroom { false };
    constexpr int classSize { inBigClassroom ? 30 : 20 };
    std::cout << "The class size is: " << classSize << '\n';
 
    return 0;
}

Для этого нет удовлетворительного оператора if/else. Вы можете подумать попробовать что-то вроде этого:

#include <iostream>
 
int main()
{
    constexpr bool inBigClassroom { false };
    if (inBigClassroom)
        constexpr int classSize { 30 };
    else
        constexpr int classSize { 20 };
    std::cout << "The class size is: " << classSize;
 
    return 0;
}

Однако это не скомпилируется, и вы получите сообщение об ошибке, что classSize не определен. Подобно тому, как переменные, определенные внутри функций, умирают в конце функции, переменные, определенные внутри инструкции if или else, умирают в конце инструкции if или else. Таким образом, classSize уже был уничтожен к тому моменту, когда мы пытаемся напечатать его значение.

Если вы хотите использовать if/else, вам нужно будет сделать что-то вроде этого:

#include <iostream>
 
int getClassSize(bool inBigClassroom)
{
    if (inBigClassroom)
        return 30;
    else
        return 20;
}
 
int main()
{
    const int classSize { getClassSize(false) };
    std::cout << "The class size is: " << classSize;
 
    return 0;
}

Это работает, потому что мы не определяем переменные внутри if или else, мы просто возвращаем значение обратно вызывающей функции, которое затем можно использовать в качестве инициализатора.

А это много лишней работы!

Тип выражений должен быть одинаковым или конвертируемым

Чтобы соответствовать проверке типов C++, оба выражения в условном операторе должны либо быть одинакового типа, либо второе выражение должно преобразовываться в тип первого выражения.

Итак, хотя вы могли ожидать, что можете написать что-то вроде этого:

#include <iostream>
 
int main()
{
	constexpr int x{ 5 };
	std::cout << (x != 5 ? x : "x is 5"); // не компилируется
 
	return 0;
}

Приведенный выше пример не компилируется. Одно из выражений является значением целочисленного типа, а другое – строковым литералом. Компилятор попытается найти способ преобразовать строковый литерал в целочисленное значение, но, поскольку он не знает, как это сделать, выдаст ошибку. В таких случаях вам придется использовать if/else.

Итак, когда следует использовать условный оператор?

Условный оператор дает нам удобный способ сделать более компактными некоторые операторы if/else. Он наиболее полезен, когда нам нужен условный инициализатор (или присваивание) для переменной или для передачи условного значения функции.

Его не следует использовать для сложных операторов if/else, поскольку он быстро становится нечитаемым и подверженным ошибкам.

Лучшая практика


Используйте условный оператор только для простых условных выражений там, где вы используете результат, и где он улучшает читаемость.

Теги

C++ / CppLearnCppОбучениеОператор (программирование)ПрограммированиеТернарный оператор

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.