Я просматриваю код, и я нашел несколько тернарных операторов. Этот код - это библиотека, которую мы используем, и она должна быть довольно быстрой.
Я думаю, что если мы будем спасать что-либо, кроме места там.
Каков ваш опыт?
Я просматриваю код, и я нашел несколько тернарных операторов. Этот код - это библиотека, которую мы используем, и она должна быть довольно быстрой.
Я думаю, что если мы будем спасать что-либо, кроме места там.
Каков ваш опыт?
Тернарный оператор не должен отличаться по производительности от хорошо написанного эквивалентного оператора 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...
Это много дополнительных вызовов функций, которые компилятор может или не может оптимизировать.
Если выражения t1
, f1
, t2
и т.д. слишком многословны для повторного ввода, создание именованных временных файлов может помочь, но затем:
Чтобы получить соответствие производительности ?
:
, вам может понадобиться использовать 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
. Оператор 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.t.c. Я считаю, что короче лучше.
Похоже, что в конце концов есть несколько значений для троичных операторов: -)
Это только если скорость кода абсолютно важна, конечно. Если/else-заявления гораздо легче читать, когда они вложены чем-то вроде (c1)? (C2)? (C3)? (C4)?: 1: 2: 3: 4. И наличие огромных выражений в качестве аргументов функции не является забавным.
Также имейте в виду, что вложенные тернарные выражения делают рефакторинг кода или отладки, помещая кучу удобных printfs() при условии - намного сложнее.
Единственное потенциальное преимущество тройных операторов над ясными, если на мой взгляд, заключается в их способности использоваться для инициализаций, что особенно полезно для const
:
например.
const int foo = (a > b ? b : a - 10);
Выполнение этого с помощью блока if/else невозможно без использования функции cal. Если у вас случается много случаев таких вещей, подобных вам, вы можете найти небольшой выигрыш от инициализации const должным образом над присваиванием с if/else. Измерьте это! Наверное, даже не измерить. Причина, по которой я склонен это делать, заключается в том, что, отмечая это, компилятор знает, когда я что-то делаю позже, что может/случайно изменить что-то, что, по моему мнению, было исправлено.
Эффективно, что я говорю, заключается в том, что троичный оператор важен для const-correctness, а const-правильность - большая привычка находиться в:
Вы предполагаете, что между ними должно быть различие, когда на самом деле существует множество языков, которые отказываются от выражения "if-else" в пользу выражения "if-else" (в этом случае они может даже не иметь тернарный оператор, который больше не нужен)
Представьте себе:
x = if (t) a else b
В любом случае, тернарный оператор является выражением в некоторых языках (C, С#, С++, Java и т.д.), которые не имеют выражений "if-else" и, следовательно, там выполняет определенную роль.
Если вы беспокоитесь об этом с точки зрения производительности, я был бы очень удивлен, если бы между ними было что-то другое.
С точки зрения взгляда, в основном это зависит от личных предпочтений. Если условие короткое и истинные/ложные части коротки, то тернарный оператор в порядке, но что-либо более продолжительное, как правило, лучше в выражении if/else (на мой взгляд).