Мы уже узнали, что операторы && и || для кастомных типов - простые функции. Для функций существует гарантия вычисления всех аргументов перед тем как функция начнет выполняться. Поэтому перегруженные версии этих операторов и не проявляют своих короткосхемных свойств. Однако операторы && и || для тривиальных типов - другое дело и имеют такие свойства. Но почему? Как это так работает в одном случае и не работает в другом? Давайте разбираться.
Во-первых(и в-единственных), операторы для тривиальных типов - это не функции. Они сразу превращаются в определенную последовательность машинных команд. Так как у нас теперь нет ограничения, что мы должны вычислить все аргументы сразу, то и похимичить можно уже знатно.
Если подумать, то логика тут очень похожа на вложенные условия. Если первое выражение правдиво, переходим в вычислению второго, если нет, то выходим из условия(это для &&). И если еще подумать, то у нас и нет никаких других средств это сделать, кроме джампов(условных переходов к метке). Покажу, во что примерно компиляторы С/С++ преобразуют выражение содержащее оператор &&. Не настаиваю на достоверность и точность. Объяснение больше для понимание происходящих процессов.
Вот есть у нас такой код
if (expr1 && expr2 && expr3) {
// cool operation
} else {
// even cooler operation
}
// the coolest operation
Он преобразуется примерно вот в такое:
if (!expr1) goto do_even_cooler_operation;
if (!expr2) goto do_even_cooler_operation;
if (!expr3) goto do_even_cooler_operation;
{
// cool operation
goto do_the_coolest_operation;
}
do_even_cooler_operation:
{
// even cooler operation
}
do_the_coolest_operation:
// the coolest operation
Что здесь происходит. Входим в первое условие и если оно ложное(то есть expr1 - true), то проваливаемся дальше в следующее условие и делаем так, пока наши выражения правдивые. Если они в итоге все оказались правдивыми, то мы входим в блок выполняющий клевую операцию и дальше прыгаем уже наружу первоначального условия и выполняем самую клевую операцию. Если хоть одно из выражений expr оказалось ложным, то мы переходим по метке и выполняем еще круче операцию и естественным образом переходим к выполнению самой крутой операции. Прикол здесь в трех условиях. Так как они абсолютно не связаны друг другом и последовательны, то следующее по счету выражение просто не будет выполняться, пока выполнение не дойдет до него. Таким образом и обеспечиваются последовательные вычисления слева направо.
То есть встроенные операторы && и || разворачиваются вот с такую гармошку условий. Надеюсь, для кого-то открыл глаза, как это работает)
See what's under the hood. Stay cool.
#compiler #cppcore