Бинарные логические операторы. Short circuit.



Хотел прояснить один момент. В этом посте я сказал, что при инстанциации шаблонов операторы && и || не могут похвастаться наличием короткой схемы, в том числе в виде fold expression. Я имел ввиду, что вам придется конкретизировать все метаклассы(ну не прям вам своими ручками, но тем не менее) для того, чтобы начать вычислять выражение. Это правда, без сомнений.



Однако я понял, что некоторые из наших дорогих подписчиков возможно могли сконфузится конфузом, что тут я говорю, что короткой схемы у них нет, а в посте про вычисления по короткой схеме я ставлю в пример эти два оператора. Вроде бы противоречие. Но нет. Тут довольно тонкая грань.



Опять же, без сомнений, && и || что в обычном виде, что в форме fold expression выполняют вычисления по короткой схеме. Они просто обязаны инстанциировать все шаблонные параметры/параметры из пака шаблонных аргументов, чтобы начать выполняться и начать проявлять свои short circuit свойства. То есть



template <class T>

struct type_without_value

{

};



template <class T1, class T2>

constexpr auto numbers = (std::is_integral_v<T1> && type_without_value<T2>::value);



constexpr auto result = numbers<float, int>;




вот этот пример крашнется на компиляции, потому что компилятору нужно инстанцировать type_without_value<T2> и достать из него value, но он не сможет этого сделать, потому что type_without_value не содержит члена value. Но в случае удачной инстанциации этот пример:



template <class T1, class T2>

constexpr auto numbers = (std::is_integral_v<T1> && std::is_integral_v<T2>);



constexpr auto result = numbers<float, int>;




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



Это можно продемонстрировать на следующем примере. Вот такой код успешно соберется и выполнится:



constexpr bool is_even(int value) noexcept {

return (value % 2 == 0) ? true : throw std::logic_error("Don't throw me around, you bastard!");

}



template <class T>

constexpr auto numbers = (std::is_integral_v<T> && is_even(1));



constexpr auto result = numbers<float>;

// constexpr bool fail = is_even(1);




Как видите, при выполнении функции is_even, если в нее попадет нечетное число, то бросится исключение. Исключение, брошенное в compile-time, прерывает компиляцию.



Но в крайнем примере все пройдет хорошо, потому что is_even ни разу не выполнится, как раз из-за короткосхемности оператора &&!



При этом, если раскомментить последнюю строчку, то компиляция зафейлится. Потому что функция все-таки начнет выполняться во время компиляции, но из нее вылетит исключение, которое и прервет эту самую компиляцию.



Вот такие дела. Надеюсь, я не зря волновался и для кого-то получше прояснил ситуацию.



Explain things clearly. Stay cool.



#template #compiler