Как стать автором
Обновить

Комментарии 17

template< typename T, IsSigned< T > = true >
T myAbs( T val );
Классно выглядит, но "= true" выглядит опасным, т.к. уставший разработчик может написать
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, то такой вариант не очень подходит.

В этом случае EnsureUnsigned можно использовать так:
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) от слова совсем. Т.е. если пользователь хочет докопаться, то он это сделает и его ничего не остановит.

Т.е. если пользователь хочет докопаться, то он это сделает и его ничего не остановит.

Но зачем ему это делать? Если человек делает так — то он либо точно знает, зачем это делает и осознаёт последствия, либо вообще не понимает что такое программирование, и никогда не открывал cppreference

Я к тому, что это решение по сути ничем не отличается от того, что приведено в статье.

Кроме того, что оно страхует от непреднамеренного неправильного использования)

Можете пояснить непроснувшемуся мозгу (ака мне) почему вариант с =false чем-то отличается?
Ведь там просто выводится тип bool в случае успеха, и каким дефолтным значением переменную в параметрах шаблона инициализировать (true/false) без разницы, разве нет?

В том-то и дело, что он ничем не отличается и это мне не нравится, т.к. можно непреднамеренно написать IsSigned = false и ожидать, что эта специализация будет работать для unsigned типов.

Я считаю, что шаблонный код в продакшене часто используют те, кто не умеет его читать, и с этим приходится жить: писать так, чтобы никто с мыслью «я ничего не понял, просто поменяю true на false, вдруг сработает» не добавил еще один баг в проект.

Там еще с enum class прикольный вариант подсказали. Только я бы опять же не yes использовал, а dummy. «yes» подразумевает, что бывает «no», когда «dummy» отражает суть — заглушка.
«yes» подразумевает, что бывает «no»

Интересно, о чём будет думать программист, который напишет свой enum class no, в расчёте на то, что с ним будет другой результат? Или Вы имеете в виду, что символ no может чисто случайно оказаться в том же scope, и программист воспользуется им, не посмотрев?

Он будет думать про std::true_type, std::fasle_type и то, зачем здесь yes_type, который на первый взгляд похож на переизобретенный true_type.

Концепты — отличная штука. Это как раз то, что нужно разработчику, чтобы определять зависимости и условия для типов. Можно накладывать условия на сами типы через concept, а можно условие на все типы в функции через requires

Я правильно понял — вы придумали какую-то compile-time конструкцию, а потом в compile-же time её обошли? Ну, наверное, круто.
Но как мы можем отложить static_assert до создания шаблона? Сделайте его условие зависимым от типа!

А в gcc этот static_assert под if contexpress() работает?
А какой смысл в
( val <= -1 ) ? -val : val
вместо классического
( val < 0 ) ? -val : val
Ну да, это даже не «какой смысл вместо», это вообще неправильно. Например, double подходит и под std::is_arithmetic и под std::is_signed, угадайте что будет, если вызвать эту myAbs() с аргументом double = -0.1.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий