wtz
wtz

Reputation: 488

How is T(2) == T(1) different from std::is_same_v<T, bool> for integral types T?

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

Answers (2)

wtz
wtz

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

463035818_is_not_an_ai
463035818_is_not_an_ai

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

Related Questions