Подтвердить что ты не робот

Прелесть тернарного оператора против оператора if

Я просматриваю код, и я нашел несколько тернарных операторов. Этот код - это библиотека, которую мы используем, и она должна быть довольно быстрой.

Я думаю, что если мы будем спасать что-либо, кроме места там.

Каков ваш опыт?

4b9b3361

Ответ 1

Производительность

Тернарный оператор не должен отличаться по производительности от хорошо написанного эквивалентного оператора 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 и т.д. слишком многословны для повторного ввода, создание именованных временных файлов может помочь, но затем:

  • Чтобы получить соответствие производительности ? :, вам может понадобиться использовать 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 подчеркивает сначала ветвление, и что нужно сделать, является вторичным, в то время как троичный оператор подчеркивает, что делать с выбором значений, чтобы сделать это с.

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

Ответ 2

Ну...

Я провел несколько тестов с 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() при условии - намного сложнее.

Ответ 3

Единственное потенциальное преимущество тройных операторов над ясными, если на мой взгляд, заключается в их способности использоваться для инициализаций, что особенно полезно для const:

например.

const int foo = (a > b ? b : a - 10);

Выполнение этого с помощью блока if/else невозможно без использования функции cal. Если у вас случается много случаев таких вещей, подобных вам, вы можете найти небольшой выигрыш от инициализации const должным образом над присваиванием с if/else. Измерьте это! Наверное, даже не измерить. Причина, по которой я склонен это делать, заключается в том, что, отмечая это, компилятор знает, когда я что-то делаю позже, что может/случайно изменить что-то, что, по моему мнению, было исправлено.

Эффективно, что я говорю, заключается в том, что троичный оператор важен для const-correctness, а const-правильность - большая привычка находиться в:

  • Это экономит ваше время, позволяя компилятору помочь вам обнаружить ошибки, которые вы совершаете.
  • Это может потенциально позволить компилятору применить другие оптимизации

Ответ 4

Вы предполагаете, что между ними должно быть различие, когда на самом деле существует множество языков, которые отказываются от выражения "if-else" в пользу выражения "if-else" (в этом случае они может даже не иметь тернарный оператор, который больше не нужен)

Представьте себе:

x = if (t) a else b

В любом случае, тернарный оператор является выражением в некоторых языках (C, С#, С++, Java и т.д.), которые не имеют выражений "if-else" и, следовательно, там выполняет определенную роль.

Ответ 5

Если вы беспокоитесь об этом с точки зрения производительности, я был бы очень удивлен, если бы между ними было что-то другое.

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