Привлекательность тернарного оператора против оператора if
Я просматриваю некоторый код, и я нашел в нем несколько троичных операторов. Этот код-это библиотека, которую мы используем, и она должна быть довольно быстрой.
Я думаю, если мы сохраняем что-нибудь, кроме места там.
каков ваш опыт?
5 ответов
производительность
тернарный оператор не должен отличаться по производительности от хорошо написанного эквивалента if
/else
заявление... они вполне могут разрешаться одним и тем же представлением в абстрактном синтаксическом дереве, проходить те же оптимизации и т. д..
вещи, которые вы можете делать только с ? :
если вы инициализируете константу или ссылку или разрабатываете, какое значение использовать внутри списка инициализации члена, то if
/else
заявления не может быть использован, но ?
:
можно:
const int x = f() ? 10 : 2;
X::X() : n_(n > 0 ? 2 * n : 0) { }
факторинг для краткого кода
ключи причины использования ?
:
включить локализацию и избежать избыточного повторения других частей тех же операторов / вызовов функций, например:
if (condition)
return x;
else
return y;
... только предпочтительнее...
return condition ? x : y;
...по удобочитаемости, если иметь дело с очень неопытными программистами, или некоторые из терминов достаточно сложны, чтобы ?
:
структура теряется в шуме. В более сложных случаях как:
fn(condition1 ? t1 : f1, condition2 ? t2 : f2, condition3 ? t3 : f3);
эквивалентной if
/else
:
if (condition1)
if (condition2)
if (condition3)
fn(t1, t2, t3);
else
fn(t1, t2, f3);
else if (condition3)
fn(t1, f2, t3);
else
fn(t1, f2, f3);
else
if (condition2)
...etc...
это много дополнительных вызовов функций, которые компилятор может или не может оптимизировать.
не могут ли названные временные объекты улучшить чудовищность if/else выше?
если выражения t1
, f1
, t2
etc. слишком многословны для повторного ввода, создание именованных временных файлов может помочь, но затем:
чтобы получить соответствие производительности
?
:
возможно, вам придется использоватьstd::move
, за исключением случаев, когда одно и то же временное передается двум&&
параметры в вызываемой функции: тогда вы должны избежать этого. Это более сложно и подвержено ошибкам.c
?
x:
y оценивает c тогда либо, но не оба из x и y, что позволяет с уверенностью сказать, что тестовый указатель неnullptr
перед использованием, обеспечивая некоторое резервное значение / поведение. Код получает только побочные эффекты того из x и y на самом деле выбраны. С названными временными, вам может понадобитьсяif
/else
или?
:
внутри инициализация нежелательного выполнения кода или выполнение кода чаще, чем требуется.
функциональная разница: объединяющий тип результата
считаем:
void is(int) { std::cout << "int\n"; }
void is(double) { std::cout << "double\n"; }
void f(bool expr)
{
is(expr ? 1 : 2.0);
if (expr)
is(1);
else
is(2.0);
}
в версии условного оператора выше,1
проходит стандартное преобразование к double
чтобы тип соответствовал 2.0
, т. е. is(double)
перегрузка вызывается даже для true
/1
ситуации. The if
/else
оператор не запускает это конверсии:true
/1
филиал звонки is(int)
.
вы не можете использовать выражения с общего типа void
в условном операторе, тогда как они действительны в операторах под if
/else
.
акцент: значение-выбор до / после действия, требующего значений
Есть другой акцент:
Теги if
/else
заявление подчеркивает первый ветвления и что надо сделать вторичный, в то время как тернарный оператор подчеркивает, что нужно сделать над выбором значений для этого.
в разных ситуациях любой из них может лучше отражать "естественную" точку зрения программиста на код и облегчать его понимание, проверку и обслуживание. Вы можете выбрать один из них на основе порядка, в котором вы учитываете эти факторы при написании кода - если вы запустили "что-то делать" , то найдите вас может использовать одно из нескольких (или нескольких) значений для этого, ?
:
это наименее разрушительный способ выразить это и продолжить кодирование "потока".
хорошо...
Я сделал несколько тестов с GCC и вызовом этой функции:
add(argc, (argc > 1)?(argv[1][0] > 5)?50:10:1, (argc > 2)?(argv[2][0] > 5)?50:10:1, (argc > 3)?(argv[3][0] > 5)?50:10:1);
полученный код ассемблера с gcc-O3 имел 35 инструкций.
эквивалентный код с if / else + промежуточными переменными имел 36. С вложенным if / else, используя тот факт, что 3 > 2 > 1, я получил 44. Я даже не пытался расширить это на отдельные вызовы функций.
теперь я не делал никакого анализа производительности, и я не делал проверку качества результата ассемблерный код, но в чем-то простом, как это, без циклов e.т. c. Думаю, чем короче, тем лучше.
похоже, что есть некоторое значение для троичных операторов в конце концов: -)
это только в том случае, если скорость кода абсолютно важна, конечно. Операторы If/else намного легче читать при вложенности, чем что-то вроде (c1)?(c2)?(c3)?(c4)?:1:2:3:4. И наличие огромных выражений в качестве аргументов функции-это не удовольствие.
также имейте в виду, что вложенные тройные выражения делают рефакторинг кода-или отладку, помещая кучу удобных printfs () в условие - намного сложнее.
единственной потенциальной выгодой для тернарных операторов над простыми операторами if, на мой взгляд, является их способность использоваться для инициализации, что особенно полезно для const
:
Э. Г.
const int foo = (a > b ? b : a - 10);
делать это с блоком if/else невозможно без использования функции cal. Если у вас есть много случаев таких вещей const, вы можете обнаружить, что есть небольшая выгода от инициализации const должным образом над назначением с if/else. Измерьте его! Хотя, вероятно, даже не будет измеримым. Причина, по которой я склонен это делать, заключается в том, что, пометив его const, компилятор знает, когда я делаю что-то позже, что может/случайно изменить что-то, что я думал, было исправлено.
фактически я говорю, что тернарный оператор важен для const-правильности, а правильность const-отличная привычка:
- это экономит много вашего времени, позволяя компилятору помочь вам обнаружить ошибки, которые вы make
- это потенциально может позволить компилятору применять другие оптимизации
вы предполагаете, что есть должно быть различие между ними, когда на самом деле существует ряд языков, которые отказываются от утверждения "если-еще" в пользу выражения "если-еще" (в этом случае они могут даже не иметь тернарного оператора, который больше не нужен)
представьте себе:
x = if (t) a else b
в любом случае,тернарный оператор является выражением на некоторых языках (C,C#, C++, Java и т. д.), которые делают не есть выражения "if-else" и, таким образом, это выполняет определенную роль там.
Если вы беспокоитесь об этом с точки зрения производительности, я был бы очень удивлен, если бы между ними было что-то другое.
с точки зрения взгляда и чувства это в основном сводится к личным предпочтениям. Если условие короткое, а истинные / ложные части короткие, то тернарный оператор в порядке, но что-то длиннее, как правило, лучше в выражении if/else (на мой взгляд).