问题
Given:
template<typename T>
inline bool f( T n ) {
return n >= 0 && n <= 100;
}
When used with an unsigned
type generates a warning:
unsigned n;
f( n ); // warning: comparison n >= 0 is always true
Is there any clever way not to do the comparison n >= 0
when T
is an unsigned
type? I tried adding a partial template specialization:
template<typename T>
inline bool f( unsigned T n ) {
return n <= 100;
}
but gcc 4.2.1 doesn't like that. (I didn't think that kind of partial template specialization would be legal anyway.)
回答1:
You can use enable_if
with the is_unsigned
type trait:
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;
}
You can find enable_if
and is_unsigned
in the std
or std::tr1
namespaces if your compiler supports C++0x or TR1, respectively. Otherwise, Boost has an implementation of the type traits library, Boost.TypeTraits. The boost implementation of enable_if
is a little different; boost::enable_if_c
is similar to the TR1 and C++0x enable_if
.
回答2:
You can take advantage of the wrap-around behavior of unsigned integers.
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)>());
}
It's important not to say >= 0
, to avoid a warning again. The following appears to trick GCC too
template<typename T>
inline bool f( T n ) {
return (n == 0 || n > 0) && n <= 100;
}
回答3:
Starting in c++17 with the introduction of if constexpr you don't even need to provide specializations for this. Unlike a normal if statement the code in the if constexpr
will be discarded (not compiled) if the expression is not true. That means you can rewrite your function like
template<typename T>
inline bool f( T n )
{
if constexpr (std::is_unsigned_v<T>)
return n <= 100;
else
return n >= 0 && n <= 100;
}
回答4:
Is there any clever way not to do the comparison n >= 0 when T is an unsigned type? I tried adding a partial template specialization:
The optimizer should drop the code for the compare since it detected the condition.
For Clang, add -Wno-tautological-compare
to squash the warning. For GCC/G++, add -Wno-type-limits
to squash the warning.
If you are using a compiler that support pragma diagnostic {push|pop}
you can:
#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
Also see Comparison is always false due to limited range…
回答5:
You can implement a special template function implementation for unsigned
type like:
template<class T> bool f(T val);
template<> bool f<unsigned>(unsigned val);
UPDATE Unsigned flag
You can implement different implementations for all unsigned types you'd like to use or add a bool
flag like:
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;
来源:https://stackoverflow.com/questions/4762110/partial-template-specialization-based-on-signed-ness-of-integer-type