Комментарии 17
Классно выглядит, но "= true" выглядит опасным, т.к. уставший разработчик может написатьtemplate< typename T, IsSigned< T > = true > T myAbs( T val );
IsSigned<T> = false
и это скомпилируется не так, как он ожидал. Как по мне, легче использовать trailing return type:
template <typename T>
using EnsureUnsigned = std::enable_if_t< !std::is_signed_v<T>, T >;
template <typename T>
auto myAbs(T val) -> EnsureUnsigned<T>
{
return val;
}
В этом случае никто не попытается написать "=false", и никому не надо думать о том, можно ли так написать.
С возвращаемым значением есть одна проблемка. В приведенном выше простом примере так можно делать. Однако если тип возвращаемого значения auto
, то такой вариант не очень подходит.
template <typename T, EnsureUnsigned<T>* dummy = nullptr>
auto myAbs(T val)
{
return val;
}
И это снова на одну граблю меньше.UPD: на всякий случай расшифрую, что я поднимаю под граблями в этом случае.
Выражение «X x = val» интуитивно воспринимается, как будто туда можно впихнуть любое эквивалентное значение val: true/false, или цифру, или строку, или указатель.
При этом, увидев указатель, программист либо подумает, что параметр — это хак, либо не поймет ничего и станет разбираться что, как и зачем туда можно запихнуть. Но скорее всего, не станет разбираться и скопипастит как есть.
А увидев «IsUnsigned = true» может показаться, что можно написать «IsUnsigned = false». И это скомпилируется.
Можно и так, только это случай не trailing return type, а лишь улучшение описанной идеи. Опять же, это не защищает от хаков типа myAbs<int, nullptr>(v)
от слова совсем. Т.е. если пользователь хочет докопаться, то он это сделает и его ничего не остановит.
Можете пояснить непроснувшемуся мозгу (ака мне) почему вариант с =false чем-то отличается?
Ведь там просто выводится тип bool в случае успеха, и каким дефолтным значением переменную в параметрах шаблона инициализировать (true/false) без разницы, разве нет?
Я считаю, что шаблонный код в продакшене часто используют те, кто не умеет его читать, и с этим приходится жить: писать так, чтобы никто с мыслью «я ничего не понял, просто поменяю true на false, вдруг сработает» не добавил еще один баг в проект.
Там еще с enum class прикольный вариант подсказали. Только я бы опять же не yes использовал, а dummy. «yes» подразумевает, что бывает «no», когда «dummy» отражает суть — заглушка.
«yes» подразумевает, что бывает «no»
Интересно, о чём будет думать программист, который напишет свой enum class no, в расчёте на то, что с ним будет другой результат? Или Вы имеете в виду, что символ no
может чисто случайно оказаться в том же scope, и программист воспользуется им, не посмотрев?
enum class yes_t {} inline constexpr yes{};
template< typename T, IsSigned< T > = yes >
T myAbs( T val );
Концепты — отличная штука. Это как раз то, что нужно разработчику, чтобы определять зависимости и условия для типов. Можно накладывать условия на сами типы через concept
, а можно условие на все типы в функции через requires
Но как мы можем отложить static_assert до создания шаблона? Сделайте его условие зависимым от типа!
А в gcc этот static_assert под if contexpress() работает?
( val <= -1 ) ? -val : val
вместо классического( val < 0 ) ? -val : val
Как сделать SFINAE изящным и надежным