|
3 | 3 |
|
4 | 4 | #include <cstddef>
|
5 | 5 | #include <iterator>
|
| 6 | +#include <optional> |
6 | 7 | #include <tuple>
|
7 | 8 | #include <type_traits>
|
8 | 9 | #include <utility>
|
@@ -800,11 +801,40 @@ template<typename Type>
|
800 | 801 | * @endcond
|
801 | 802 | */
|
802 | 803 |
|
803 |
| -/*! @copydoc is_equality_comparable */ |
| 804 | +// MSVC's 'std::optional<>::operator==' is not SFINAE friendly because of its noexcept specifier: |
| 805 | +// |
| 806 | +// template <class _Ty1, class _Ty2> |
| 807 | +// constexpr bool operator==(const optional<_Ty1>& _Left, const optional<_Ty2>& _Right) noexcept(noexcept(*_Left == *_Right)) |
| 808 | +// |
| 809 | +// The noexcept specifier is not part of a function's immediate context, so substitution failures within it make the program ill-formed. |
| 810 | +// Both Clang and GCC raise an error when this 'operator==' is used in a 'void_t' expression. MSVC does not raise an error. |
| 811 | +// |
| 812 | +// Specializing 'is_equality_comparable' for 'optional<Type>' does not fix this error, as the 'is_equality_comparable<Type>' specialization is |
| 813 | +// still evaluated and its 'void_t' expression raises an error. To fix this, 'is_equality_comparable<Type>' and 'is_equality_comparable<optional<Type>>' |
| 814 | +// delegate the 'void_t' logic to 'is_equality_comparable_sfinae_fix'. optional<>'s 'operator==' is now never invoked, working around the error. |
| 815 | +// |
| 816 | +// Any template types that are not SFINAE-friendly can use 'is_equality_comparable_sfinae_fix'. Given template type T, specialize 'is_equality_comparable' |
| 817 | +// for 'T<Type>' and inherit from 'is_equality_comparable_sfinae_fix<T<Type>, Type>`. |
| 818 | + |
| 819 | +template <typename OuterType, typename Type, typename = void> |
| 820 | +struct is_equality_comparable_sfinae_fix : std::false_type {}; |
| 821 | + |
| 822 | +template<typename OuterType, typename Type> |
| 823 | +struct is_equality_comparable_sfinae_fix<OuterType, Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>> |
| 824 | + : std::bool_constant<internal::maybe_equality_comparable<OuterType>(choice<2>)> {}; |
| 825 | + |
804 | 826 | template<typename Type>
|
805 |
| -struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>> |
| 827 | +struct is_equality_comparable_sfinae_fix<Type, Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>> |
806 | 828 | : std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
|
807 | 829 |
|
| 830 | +/*! @copydoc is_equality_comparable */ |
| 831 | +template<typename Type> |
| 832 | +struct is_equality_comparable<Type> : is_equality_comparable_sfinae_fix<Type, Type> {}; |
| 833 | + |
| 834 | +/*! @copydoc is_equality_comparable */ |
| 835 | +template<typename Type> |
| 836 | +struct is_equality_comparable<std::optional<Type>> : is_equality_comparable_sfinae_fix<std::optional<Type>, Type> {}; |
| 837 | + |
808 | 838 | /*! @copydoc is_equality_comparable */
|
809 | 839 | template<typename Type, auto N>
|
810 | 840 | struct is_equality_comparable<Type[N]>: std::false_type {};
|
|
0 commit comments