static_assert и if constexpr



Вернемся к прошлому примеру с функцией to\_str, но сделаем ее немножко безопасней.



template <typename T>

std::string to_str(T t) {

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

return t;

else if constexpr (std::is_arithmetic_v<T>)

return std::to_string(t);

else

static_assert(false, "cannot convert type to std::string");

}





Все просто. Функция std::to_string имеет перегрузки только для тривиальных арифметических типов, поэтому чтобы ее использовать и получить ошибку, нужно провести проверку на эту арифметичность. Если переданный в функцию to\_str тип не конструируется в строку или не является арифметическим типом, то не совсем понятно, как такой тип переводить в строку. Точнее так: в каждом конкретном случае мы можем сказать как, но общего поведения нет. Поэтому, чтобы детектировать такие нештатные ситуации, я поставлю ассерт времени компиляции.



И вроде всё хорошо, но да не всё...



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



Тут в чем прикол. Согласно стандарту, если хотя бы в одной из веток if constexpr не может быть сгенерировано ни одной валидной специализации, то программа - ill-formed и это ошибка. И вправду, false всегда будет false, как бы мы там не тужились родить что-то валидное.



Но даже сам cppreference предлагает нам способ обойти эту неприятную бяку. Мы можем подставить вместо false шаблонное constexpr типазависимое выражение, которое будет возвращать false. И тогда все магическим образом заработает.



template<typename>

inline constexpr bool dependent_false_v = false;



template <typename T>

std::string to_str(T t) {

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

return t;

else if constexpr (std::is_arithmetic_v<T>)

return std::to_string(t);

else

static_assert(dependent_false_v<T>, "cannot convert type to std::string");

}





Видимо из-за того, что шаблон можно вручную специализировать, то теоритически возможна специализация, которая будет возвращать true. И даже если она еще не инстанцирована, то возможность-то остается. Поэтому компилятор пропускает такую конструкцию.



Однако ситуация изменилась с приходом С++23, но об этом мы поговорим завтра.



Find loopholes in the system. Stay cool.



#cpp17 #template