Частичная специализация шаблона основана на «подписи» целочисленного типа? - PullRequest
21 голосов
/ 21 января 2011

Дано:

template<typename T>
inline bool f( T n ) {
  return n >= 0 && n <= 100;
}   

При использовании с типом unsigned выдается предупреждение:

unsigned n;
f( n ); // warning: comparison n >= 0 is always true

Есть ли какой-нибудь умный способ не проводить сравнение n >= 0, когда T имеет тип unsigned? Я попытался добавить частичную специализацию шаблона:

template<typename T>
inline bool f( unsigned T n ) {
  return n <= 100;
}   

но gcc 4.2.1 это не нравится. (Я не думал, , что частичная специализация шаблонов будет в любом случае законной.)

Ответы [ 5 ]

24 голосов
/ 21 января 2011

Вы можете использовать enable_if с чертой типа is_unsigned:

template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, bool>::type f(T n)
{
    return n <= 100;  
}

template <typename T>
typename std::enable_if<!std::is_unsigned<T>::value, bool>::type f(T n)
{
    return n >= 0 && n <= 100;  
}

Вы можете найти enable_if и is_unsigned в пространствах имен std или std::tr1, если ваш компилятор поддерживаетC ++ 0x или TR1 соответственно.В противном случае Boost имеет реализацию библиотеки признаков типа Boost.TypeTraits .Реализация надстройки enable_if немного отличается;boost::enable_if_c похож на TR1 и C ++ 0x enable_if.

15 голосов
/ 21 января 2011

Вы можете воспользоваться обходным поведением целых чисел без знака.

template<bool> struct bool_ { };

template<typename T>
inline bool f( T n, bool_<false> ) {
  return n >= 0 && n <= 100;
}

template<typename T>
inline bool f( T n, bool_<true> ) {
  return n <= 100;
}

template<typename T>
inline bool f( T n ) {
  return f(n, bool_<(static_cast<T>(-1) > 0)>());
}   

Важно не говорить >= 0, чтобы избежать предупреждения снова.Похоже, что трюк GCC тоже

template<typename T>
inline bool f( T n ) {
  return (n == 0 || n > 0) && n <= 100;
}   
3 голосов
/ 12 октября 2018

Начиная с с введением if constexpr вам даже не нужно указывать специализацию для этого.В отличие от обычного оператора if, код в if constexpr будет отброшен (не скомпилирован), если выражение неверно.Это означает, что вы можете переписать свою функцию как

template<typename T>
inline bool f( T n ) 
{
    if constexpr (std::is_unsigned_v<T>)
        return n <= 100;
    else
        return n >= 0 && n <= 100;
}   
1 голос
/ 06 июля 2015

Есть ли какой-нибудь умный способ не выполнять сравнение n> = 0, когда T является беззнаковым типом?Я попытался добавить частичную специализацию шаблона:

Оптимизатор должен отбросить код для сравнения, поскольку он обнаружил условие.

Для Clang добавьте -Wno-tautological-compare, чтобы убрать предупреждение.Для GCC / G ++ добавьте -Wno-type-limits для подавления предупреждения.

Если вы используете компилятор, поддерживающий pragma diagnostic {push|pop}, вы можете:

#if (GCC_VERSION >= 40600) || (LLVM_CLANG_VERSION >= 10700) || (APPLE_CLANG_VERSION >= 20000)
# define GCC_DIAGNOSTIC_AVAILABLE 1
#endif    

#if MSC_VERSION
# pragma warning(push)
# pragma warning(disable: 4389)
#endif

#if GCC_DIAGNOSTIC_AVAILABLE
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-compare"
# if (LLVM_CLANG_VERSION >= 20800) || (APPLE_CLANG_VERSION >= 30000)
#  pragma GCC diagnostic ignored "-Wtautological-compare"
# elif (GCC_VERSION >= 40300)
#  pragma GCC diagnostic ignored "-Wtype-limits"
# endif
#endif

template<typename T>
inline bool f( T n ) {
  return n >= 0 && n <= 100;
}

#if GCC_DIAGNOSTIC_AVAILABLE
# pragma GCC diagnostic pop
#endif

#if MSC_VERSION
# pragma warning(pop)
#endif

Также см. Сравнениевсегда ложно из-за ограниченного диапазона…

0 голосов
/ 21 января 2011

Вы можете реализовать специальную реализацию шаблонной функции для типа unsigned, например:

template<class T> bool f(T val);
template<> bool f<unsigned>(unsigned val);

ОБНОВЛЕНИЕ Флаг без знака

Вы можете реализовать различные реализации для всех без знакатипы, которые вы хотели бы использовать или добавить флаг bool, например:

template <class T, bool U> bool f(T val)
{
        if (U)
                return val <= 100;
        else
                return (val >=0 ) && (val <= 100);
}

...

cout << f<int, false>(1) << endl;
cout << f<int, false>(-1) << endl;
cout << f<char, true>(10) << endl;
...