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

Условный тернарный оператор позволяет описать в едином выражении два варианта вычислений в зависимости от некоторого условия. Синтаксис оператора такой:

condition ? expression_true : expression_false

Логические условие должно быть указано в первом операнде condition. Это может быть произвольное сочетание операций сравнения и логических операций. Обе ветви обязательно должны присутствовать.

Если условие истинно, будет вычисляться выражение expression_true, а если ложно — expression_false.

Данный оператор гарантирует, что только одно из выражений expression_true и expression_false выполнится.

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

Обратите внимание, что результат обработки выражений в MQL5 всегда представляет собой RValue (в C++, если в выражениях стоят только LValue, то и результатом оператора также будет LValue). Так, следующий код компилируется в C++, но выдает ошибку в MQL5:

int x1y1; ++(x1 > y1 ? x1 : y1); // '++' - l-value required

Условные операторы могут быть вложенными, то есть в качестве условия или любой из веток (expression_true и expression_false) разрешено использовать другой условный оператор. При этом визуально может быть не всегда понятно, к чему относятся условия (если не используются круглые скобки для явного обозначения группировки). Рассмотрим примеры из ExprConditional.mq5.

int x = 1y = 2z = 3p = 4q = 5f = 6h = 7;
int r0 = x > y ? z : p != 0 && q != 0 ? f / (p + q) : h// 0 = f / (p + q)

В данном случае первое логическое условие представляет собой сравнение x > y. При его истинности выполняется ветвь с переменной z. При его ложности проверяется дополнительное логическое условие p != 0 && q != 0, также с двумя вариантами выражений.

Ниже приведено еще несколько операторов, в которых заглавными буквами обозначены логические условия, а строчными — варианты расчета. Здесь для простоты сделано, что все они — переменные (из примера выше). В реальности каждая из трех компонент бывает более витиеватым выражением.

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

bool A = falseB = falseC = true;
int r1 = A ? x : C ? p : q;                              // 4
int r2 = A ? B ? x : y : z;                              // 3
int r3 = A ? B ? C ? p : q : y : z;                      // 3
int r4 = A ? B ? x : y : C ? p : q;                      // 4
int r5 = A ? f : h ? B ? x : y : C ? p : q;              // 2

Поскольку оператор имеет свойство правой ассоциативности, составное выражение разбирается справа налево, то есть самая правая конструкция с тремя операндами, объединенными символами '?' и ':', становится операндом внешнего условия, которое записано левее. Далее с учетом этой "подмены", выражение снова разбирается справа налево и так далее, пока не получится последняя полная конструкция '?:' верхнего уровня.

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

int r0 = x > y ? z : ((p != 0 && q != 0) ? f / (p + q) : h);
int r1 = A ? x : (C ? p : q); 
int r2 = A ? (B ? x : y) : z
int r3 = A ? (B ? (C ? p : q) : y) : z
int r4 = A ? (B ? x : y) : (C ? p : q); 
int r5 = (A ? f : h) ? (B ? x : y) : (C ? p : q); 

Для переменной r5 первое условие A ? f : h вычисляет логическое условие для последующего выражения, поэтому преобразуется в bool. Поскольку A равно false, значение берется из переменной h. Она не равна 0, и потому первое условие считается истинным. В результате срабатывает ветка (B ? x : y), из которой возвращается значение переменной y, так как B равно false.

В операторе обязательно должны присутствовать все 3 компонента (условие и 2 варианта), в противном случае компилятор сгенерирует ошибку "неожиданный токен" ("unexpected token"):

// ';' - unexpected token
// ';' - ':' colon sign expected
int r6 = A ? B ? x : y// не хватает альтернативы

"Токен" на языке компилятора — это неделимый фрагмент исходного кода с самостоятельным смыслом или назначением (название типа, идентификатор, символ пунктуации и т.д.). Весь исходный код разбивается компилятором на последовательность токенов. Знаки рассматриваемых операторов тоже токены. В вышеприведенном коде имеется два символа '?' и должно быть два парных им символа ':', однако он только один. Поэтому компилятор "говорит", что символ окончания инструкции ';' преждевременный, и "уточняет", чего именно не хватает: "ожидалось двоеточие" ("colon sign expected").

Из-за того, что условный оператор имеет очень низкий приоритет (13 в полной таблице, см. Приоритеты операций), рекомендуется заключать его в скобки. Так проще избежать ситуаций, когда операнды условного оператора могли бы быть "захвачены" соседними операциями с более высоким приоритетом. Например, если требуется рассчитать значение некоторой переменной w через сумму двух тернарных операторов, прямолинейный подход мог бы выглядеть так:

int w = A ? f : h + B ? x : y;                           // 1

Это будет работать иначе, чем задумывалось. Сумма h + B из-за более высокого приоритета рассматривается как единое выражение. Учитывая разбор справа налево, эта сумма оказывается в роли условия и приводится к типу bool, о чем компилятор даже выдает предупреждение "выражение не является логическим" ("expression not boolean"). Трактовку компилятора можно для наглядности выделить скобками:

int w = A ? f : ((h + B) ? x : y);                       // 1

Для решения проблемы необходимо расставить скобки по-своему.

int v = (A ? f : h) + (B ? x : y);                       // 9

Глубокая вложенность условных операторов негативно сказывается на понятности кода. Следует избегать уровней вложенности больше двух-трех.