Skip to content

Commit fb1d168

Browse files
committed
MSVC's optional<>::operator== does not support SFINAE
1 parent 75bff4d commit fb1d168

File tree

1 file changed

+32
-2
lines changed

1 file changed

+32
-2
lines changed

src/entt/core/type_traits.hpp

+32-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <cstddef>
55
#include <iterator>
6+
#include <optional>
67
#include <tuple>
78
#include <type_traits>
89
#include <utility>
@@ -800,11 +801,40 @@ template<typename Type>
800801
* @endcond
801802
*/
802803

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+
804826
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>())>>
806828
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
807829

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+
808838
/*! @copydoc is_equality_comparable */
809839
template<typename Type, auto N>
810840
struct is_equality_comparable<Type[N]>: std::false_type {};

0 commit comments

Comments
 (0)