Reputation: 488
I would like to ensure that an integral type is not bool
in static_assert
. I found in the source code of libstdc++ that it uses _Mn(2) != _Mn(1)
for this purpose:
template<typename _Mn, typename _Nn>
constexpr common_type_t<_Mn, _Nn>
gcd(_Mn __m, _Nn __n) noexcept
{
static_assert(is_integral_v<_Mn>, "std::gcd arguments must be integers");
static_assert(is_integral_v<_Nn>, "std::gcd arguments must be integers");
static_assert(_Mn(2) != _Mn(1), "std::gcd arguments must not be bool"); // HERE
static_assert(_Nn(2) != _Nn(1), "std::gcd arguments must not be bool");
using _Up = make_unsigned_t<common_type_t<_Mn, _Nn>>;
return __detail::__gcd(__detail::__absu<_Up>(__m),
__detail::__absu<_Up>(__n));
}
It seems that libstdc++ is intentionally avoiding the more apparent form !is_same_v<_Mn, bool>
. How are the two forms different? Which one should I use for the same purpose?
@KamilCuk pointed out that _Mn(2) != _Mn(1)
was once !is_same_v<remove_cv_t<_Mn>, bool>
in libstdc++. (Commit)
Upvotes: 0
Views: 223
Reputation: 488
It turns out that cv-qualified non-reference bool
types (const bool
, volatile bool
and const volatile bool
) pass std::is_integral_v<T> && !std::is_same_v<T, bool>
but fail std::is_integral_v<T> && T(2) == T(1)
. std::remove_cv_t<T>
should be used instead of T
in std::is_same_v
to handle these types correctly.
Upvotes: 1
Reputation: 123114
There is only one type that passes the is_integral_v<_Mn>
assert but would fail the _Mn(1) == _Mn(2)
check, and that is bool
. The check is ok to identify bool
in this special circumstance, when only types that are std::integral_v == true
are considered.
Note that _Mn(1) == _Mn(2)
can detect and cv variant of bool
, hence the right comparison is std::is_same_v<std::remove_cv_t<Mn>,bool>
(Thanks to Jarod42 to point that out).
However, when not considering only std::integral_v == true
the _Mn(1) == _Mn(2)
check is not sufficient to know that it is bool
or not. It could be any other type.
When you want to know if two types are the same you should use std::is_same
.
PS: In general I do not advise to learn how to write C++ code from the implementation of the standard library. By no means it is bad code, but it is code that lives in a different domain. Most notably, things that are undefined in user code can be completely fine in the implementation (as for example identifiers like _Mn
).
Upvotes: 4