我正在编写一个能够处理任何整数类型的库函数,但我想阻止它也处理字符类型(例如
char
,char16_t
等),因为它可能会让用户感到困惑(我有一个单独的函数来处理看起来像字符的字符)。
我目前正在使用这样的代码:
template <
typename = std::enable_if_t<
std::is_integral_v<T> && !std::is_same_v<std::decay_t<T>, bool> &&
!std::is_same_v<std::decay_t<T>, char> && !std::is_same_v<std::decay_t<T>, wchar_t> &&
!std::is_same_v<std::decay_t<T>, char16_t> && !std::is_same_v<std::decay_t<T>, char32_t>>>
constexpr auto do_stuff(T curr) noexcept {
/* TODO */
}
但是,这看起来非常脆弱 - 例如,这在带有
char8_t
的 C++20 编译器上并不完全正确,即使我对 char8_t
进行了正确的功能测试,仍然相当尴尬必须列出他们都出去了。
有没有一种方法可以检测类似字符的类型,而我不需要将它们全部列出来?
你可以这样做:
template <
typename T,
std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr,
std::enable_if_t<std::is_same_v<T, std::make_signed_t<T>> || std::is_same_v<T, std::make_unsigned_t<T>>, std::nullptr_t> = nullptr
>
这接受
signed char
和 unsigned char
,同时拒绝 char
。 int8_t
是所有常见标准库实现(libstdc++、libc++ 和 MSVC 的实现)上的 signed char
。
注意不寻常的双 SFINAE。这是必要的,因为如果给定非整数类型,
std::make_[un]signed
会导致硬错误。
或者,在 C++20 中,您可以检查
std::cmp_equal
是否接受您的类型。它还拒绝 char
和 char*_t
,同时允许 [un]signed char
。
requires requires{ std::cmp_equal(T{}, T{}); }
与之前的答案类似,但具有与 C++11 兼容的类型特征:
#include <type_traits>
template<class T>
using is_actually_arithmetic = typename std::conditional<
std::is_arithmetic<T>::value,
std::is_same<
T,
typename std::conditional<
std::is_integral<T>::value,
typename std::conditional<
std::is_signed<T>::value,
std::make_signed<T>,
std::make_unsigned<T>
>::type,
std::enable_if<true, std::enable_if<true, T> >
>::type::type::type
>,
std::false_type
>::type;
template<class T>
using is_actually_integral = typename std::conditional<
std::is_integral<T>::value,
is_actually_arithmetic<T>,
std::false_type
>::type;