if constexpr



Вчера мы обсуждали, что до 17-го стандарта было довольно сложно писать шаблонный код, сценарии которого зависели от типа шаблонных параметров. Появляются проблемы с дублированием кода и с фактической невозможностью писать зависящий от конкретного типа код, потому что в каких-то ветках он может просто не скомпилироваться. Но с этими проблемами справляется фича С++17 - if constexpr.



if constexpr - расширение обычной условной конструкции с двумя дополнениями/ограничениями.



1️⃣ Условие проверяется в compile-time. И нужная ветка выбирается тоже на этом этапе. Это значит, что выражение в условии должно быть тоже вычислимым на этапе компиляции. То есть должно быть constexpr. Это несколько ограничивает его функционал(например constexpr функции могут вычисляться и во время компиляции, и во время выполнения). Хотя учитывая, что конструкция используется в шаблонном коде, для разделения поведения в зависимости от шаблонного параметра, и других кейсов я особо не вижу, то это не совсем и ограничения.



2️⃣Если одна из веток условий выбрана, то другая ветка будет отброшена(discarded) и она не будет компилироваться!



Благодаря этим особенностям, наш пример с функцией to_str будет выглядеть вот так:



template <typename T>

std::string to_str(T t) {

if constexpr (std::is_same_v<T, std::string>) // строка или преобразуемый в строку

return t;

else

return std::to_string(t);

}




А пример со сравнением чисел так:



template <class T>

constexpr T absolute(T arg) {

return arg < 0 ? -arg : arg;

}



template <class T>

constexpr auto precision_threshold = T(0.000001);



template <class T>

constexpr bool is_equal(T a, T b) {

if constexpr (is_floating_point_v<T>)

return absolute(a - b) < precision_threshold<T>;

else

return a == b;}




Стоит сделать важную оговорку: если ветка не участвует в компиляции, это не значит, что там можно все, что угодно писать. Это вам не препроцессор, который полностью стирает строчки без следа. В любой ветке код должен быть корректным. Например, вот скомпилировав такой код:



template <typename T>

std::string to_str(T t) {

if constexpr (std::is_same_v<T, std::string>)

Life_without_coding();

else

return std::to_string(t);

}



auto str = to_str(5);




Получим ошибку: error: use of undeclared identifier 'Life_without_coding'. Эх. Не прожить нам без кода...



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



После того, как компилятор проверит отсутствие ошибок, он просто не будет дальше посылать отброшенную ветку на генерацию кода. Примерно так это и работает.



Choose the right path. Stay cool.



#cpp17 #template #compiler