Секреты тернарного оператора
Каждый уважающий себя программист С\С++ знает что такое тернарный оператор и большинство использовало его хотя бы раз в своих программах. Но знаете ли вы все секреты тернарного оператора? Какие потенциальные опасности сопряжены с его использованием и какие, казалось бы не связанные с его прямым предназначением, возможности в нем таятся? Эта статья дает вам возможность проверить свои знания и, возможно, узнать что-то новое.
Начнем с небольшого теста.
Скомпилируется ли следующий код? Объясните почему.
1.
Какой будет вывод у следующего кусочка? Почему?
6.
Какие значения будут у переменных a, b и c в результате выполнения следующего кода? Почему?
7.
8. Назовите ситуацию, где нельзя использовать if <. >else , но можно тернарный оператор.
9. Какие потенциальные опасности скрываются в использовании тернарного оператора? В чем их причина?
10. Какие неожиданные использования тернарного оператора приходят вам в голову?
Объяснение
Итак, начнем. Тернарный оператор выделяется из ряда других операторов в С++. Его называют «conditional expression«. Ну а так как это expression, выражение, то как у каждого выражения, у него должен быть тип и value category. Собственно, ответив на вопросы какой тип и value category у тернарных операторов в каждом из первых семи вопросов теста, мы легко решим поставленные задачи.
Здесь начинается самое интересное. Оказывается типом тернарного оператора будет наиболее общий тип его двух последних операндов. Что значит наиболее общий? Это легче всего пояснить на примерах. У int и short общим типом будет int.
У A и B в следующем фрагменте общим типом будет также int.
Т.е. наиболее общий тип это такой тип, к которому могу быть приведены оба операнда. Вполне могут быть ситуации, когда общего типа нет. Например у
общего типа нет, и следующий фрагмент вообще не скомпилируется
Так. С типом тернарного оператора мы немного разобрались. Осталось решить вопрос с value category. Тут действует следующее правило: если в тернарном операторе происходит преобразование типов к наиболее общему, то тернарный оператор — rvalue. Если же нет, то lvalue. Теперь когда мы знаем то, что мы знаем, мы легко ответим на первые 7 вопросов.
Ответы
Как нам уже известно, второй и третий операнд тернарного оператора приводятся к наиболее общему типу. В данном случае это const char*. Но объект String(«dcba») уничтожится в конце выражения и s будет указывать на невалидную память. В лучшем случае программа упадет при попытке в дальнейшем использовать s. В худшем будет выдавать неверные результаты, вызывая недовольство у заказчика и головную боль у программиста.
«Необычное» использование тернарного оператора
Тернарный оператор можно использовать для определения общего типа двух и более типов. А это, в свою очередь, можно использовать, например, для определения приводится ли один тип к другому.
На самом деле, если знать свойства тернарного оператора, такое использование практически напрашивается само собой. Необычным здесь, пожалуй является лишь то, что он используется не по прямому назначению, т.е. не для выбора одного значения из двух в зависимости от условия.
Тернарный оператор
В жизни есть много условностей, например, если тебе горит красный — стой, зелёный — иди. Так и при написании кода — надо обрабатывать много условий. В Java для работы с такими условиями предусмотрено несколько операторов. Наверно, в первую очередь на память приходит оператор if-else (который так и можно перевести если-иначе). Но есть оператор, который используют не все разработчики, хотя порой он является не менее, а в некоторых ситуациях даже более удобным, чем тот же if-else . Речь пойдет о тернарном операторе. Рассмотрим его чуть подробнее:
Этот оператор называется тернарным (тройным), так как принимает три операнда. По своей сути он похож на оператор if-else , но при этом обязательно возвращает значение.
При записи тернарного оператора используются три операнда и два символа ? и :
то есть в случае, если «условие» == true , то выполняется «выражение 1» , если «условие»== false , то выполняется «выражение 2» .
Как говорилось вначале — этот оператор по сути аналогичен оператору if-else и эту же запись можно написать так:
Посмотрим на примере:
Это же можно было бы написать и с if-else :
Результат работы одинаковый, но запись метода с использованием тернарного оператора более компактна, без ущерба для читаемости.
Использование тернарного оператора не ограничивается выбором из двух вариантов. В качестве третьего операнда можно передавать новый тернарный оператор и так далее. Например (для простоты допустим, что остаток до отпуска не больше года):
На выходе получаем (например, при rem = 4 ) — «До отпуска 4 месяца»
Кстати, для читаемости «условие» в тернарном операторе можно брать в скобки, компилятор примет и такой вариант:
Эта же запись при использовании if-else может выглядеть примерно так:
Опять же использование тернарного оператора гораздо удобнее из-за компактности кода.
Конечно, если еще увеличивать возможность вариантов, то тогда if-else наверно будет всё-таки предпочтительней, так как он становится более понятным и легко читаемым, чем тернарный оператор, описывающий все варианты в одной строке. Но при использовании двух или трёх вариантов, тернарный оператор, кажется выигрывает за счет своей компактности.
При всей простоте тернарного оператора всё-равно есть несколько нюансов, которые стоит учитывать. Например, что мы ожидаем на выходе этого метода:
Кажется, по логике на выходе должен быть x типа int который равен 6. И на самом деле на выходе мы получаем x = 6 , но только уже типа double ( x = 6.0 ). Оказывается, что хотя при таком условии до операнда y никогда дело не дойдет, но в тернарном операторе компилятор оценивает оба операнда x и y и приводит их к общему типу (в данном случае это double ). Вот почему на выходе мы получили не ожидаемый int , а double .
Посмотрим еще один пример с примитивом и классом-оберткой :
И снова хотелось на выходе увидеть null , но в данном случае мы получаем ошибку — NullPointerException . Почему метод не может вернуть x , равный null ? Всё по той же причине — компилятор оценил оба операнда и привел возвращаемое значение к типу int , а примитив конечно же не может иметь значение null .
Произведем небольшое изменение в коде (меняем тип y на Integer ):
В этот раз метод нормально отработал и вернул null . В чем причина? Теперь мы уже понимаем, что хотя операнд y и не принимает в данном случае участия в работе оператора, но может существенно влиять на то, какой тип возвращаемого значения выберет компилятор. В данном случае так как и x , и y типа Integer , то компилятор возвратил объект класса-обертки Integer , а он в отличие от примитива уже может иметь значение null .
Описание подобных ситуаций и то, какой тип будет возвращен — описаны в документации Oracle.
Итак, мы разобрались с тем, как работает тернарный оператор, а также с тем что при небольшом «ветвлении» условий его использование даже более предпочтительней, чем оператора if-else . Также мы поняли важную особенность тернарного оператора — необходимо учитывать, что компилятор анализирует типы обоих операндов, независимо от того, что участвует только один из них и устанавливает какого типа будет возвращаемое значение. Это поможет нам понять какой тип значения мы получим на выходе.
Сергей Малыгин, после окончания базового курса, май 2020
Условный (тернарный) оператор
Условный (тернарный) оператор — единственный оператор в JavaScript, принимающий три операнда: условие, за которым следует знак вопроса (?), затем выражение, которое выполняется, если условие истинно, сопровождается двоеточием (:), и, наконец, выражение, которое выполняется, если условие ложно. Он часто используется в качестве укороченного варианта условного оператора if .
Синтаксис
Параметры
Выражение, принимающее значение true или false .
Выражения, значения которых могут принадлежать любому типу.
Описание
Оператор возвращает значение выражения1 , если условие верно, и значение выражения2 в противном случае. Например, чтобы вывести сообщение, текст которого зависит от значения переменной isMember , можно использовать такое выражение:
Также можно присваивать значения переменным на основе результатов работы тернарного оператора :
Возможны множественные тернарные операции (обратите внимание: условный оператор ассоциативен справа):
Тернарные операции можно использовать и сами по себе — для выполнения различных операций:
Также возможно выполнять несколько операций на каждое сравнение, разделив их запятыми:
При присвоении значения также возможно выполнение более одной операции. В этом случае переменной будет присвоено то значение, которое стоит последним в списке значений, разделённых запятой.
Что такое тернарный оператор
В С++ есть тернарный оператор(у оператора три операнда), который позволяет в зависимости от некоторого условия(первый операнд) выполнять различные действия(второй и третий операнды). Синтаксис вызова оператора таков: условие?выражение1:выражение2 . Фактически тернарный оператор представляет собой сокращенную форму условного оператора(об условных операторах речь идет в следующей главе). Пример вызова тернарного оператора приведен в листинге 1.9.
==========> Листинг 1.9 Тернарный оператор
#include <iostream>
using namespace std;
int main()<
int n;
double x;
cout<<«Enter n x = «<<x<<«\n»;
cout << «Конец задания для самостоятельной работы!» << endl;
system («pause»); // ожидаем нажатия кнопки
return 0;
>
В программе объявляются две переменные: целочисленная переменная n типа int и переменная типа x типа double. Значение переменной n вводится с клавиатуры пользователем, а значение переменной x определяется с помощью команды x=n>0?5.4:3.2.
Дело в том, что тернарный оператор возвращает значение.
Сначала проверяется указанное первым операндом условие.
Если условие выполнено, вычисляется выражение1 после вопросительного знака. Если условие не выполнено, вычисляется выражение2 после двоеточия.
Тернарным оператором в качестве значения возвращается значение вычисленного выражения.
В программе командой x=n>0?5.4:3.2 проверяется условие n>0, и если это так, возвращается значение 5.4. В противном случае возвращается значение 3.2.
На практике в тернарных операторах используют более сложные конструкции, чем просто возвращения в качестве значения число. Обычно прибегают к вызову в тернарных операторах функций, что позволяет не просто возвращать значение в зависимости от проверяемого условия, но и выполнять целые последовательности действий.