diff --git a/src/entt/core/type_traits.hpp b/src/entt/core/type_traits.hpp index b371c8210b..81da66a986 100644 --- a/src/entt/core/type_traits.hpp +++ b/src/entt/core/type_traits.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -800,11 +801,40 @@ template * @endcond */ -/*! @copydoc is_equality_comparable */ +// MSVC's 'std::optional<>::operator==' is not SFINAE friendly because of its noexcept specifier: +// +// template +// constexpr bool operator==(const optional<_Ty1>& _Left, const optional<_Ty2>& _Right) noexcept(noexcept(*_Left == *_Right)) +// +// The noexcept specifier is not part of a function's immediate context, so substitution failures within it make the program ill-formed. +// Both Clang and GCC raise an error when this 'operator==' is used in a 'void_t' expression. MSVC does not raise an error. +// +// Specializing 'is_equality_comparable' for 'optional' does not fix this error, as the 'is_equality_comparable' specialization is +// still evaluated and its 'void_t' expression raises an error. To fix this, 'is_equality_comparable' and 'is_equality_comparable>' +// delegate the 'void_t' logic to 'is_equality_comparable_sfinae_fix'. optional<>'s 'operator==' is now never invoked, working around the error. +// +// Any template types that are not SFINAE-friendly can use 'is_equality_comparable_sfinae_fix'. Given template type T, specialize 'is_equality_comparable' +// for 'T' and inherit from 'is_equality_comparable_sfinae_fix, Type>`. + +template +struct is_equality_comparable_sfinae_fix : std::false_type {}; + +template +struct is_equality_comparable_sfinae_fix() == std::declval())>> + : std::bool_constant(choice<2>)> {}; + template -struct is_equality_comparable() == std::declval())>> +struct is_equality_comparable_sfinae_fix() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable : is_equality_comparable_sfinae_fix {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable> : is_equality_comparable_sfinae_fix, Type> {}; + /*! @copydoc is_equality_comparable */ template struct is_equality_comparable: std::false_type {};