Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSVC's optional<>::operator== does not support SFINAE #9

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions src/entt/core/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <cstddef>
#include <iterator>
#include <optional>
#include <tuple>
#include <type_traits>
#include <utility>
Expand Down Expand Up @@ -800,11 +801,40 @@ template<typename Type>
* @endcond
*/

/*! @copydoc is_equality_comparable */
// MSVC's 'std::optional<>::operator==' is not SFINAE friendly because of its noexcept specifier:
//
// template <class _Ty1, class _Ty2>
// 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<Type>' does not fix this error, as the 'is_equality_comparable<Type>' specialization is
// still evaluated and its 'void_t' expression raises an error. To fix this, 'is_equality_comparable<Type>' and 'is_equality_comparable<optional<Type>>'
// 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<Type>' and inherit from 'is_equality_comparable_sfinae_fix<T<Type>, Type>`.

template <typename OuterType, typename Type, typename = void>
struct is_equality_comparable_sfinae_fix : std::false_type {};

template<typename OuterType, typename Type>
struct is_equality_comparable_sfinae_fix<OuterType, Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::bool_constant<internal::maybe_equality_comparable<OuterType>(choice<2>)> {};

template<typename Type>
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
struct is_equality_comparable_sfinae_fix<Type, Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};

/*! @copydoc is_equality_comparable */
template<typename Type>
struct is_equality_comparable<Type> : is_equality_comparable_sfinae_fix<Type, Type> {};

/*! @copydoc is_equality_comparable */
template<typename Type>
struct is_equality_comparable<std::optional<Type>> : is_equality_comparable_sfinae_fix<std::optional<Type>, Type> {};

/*! @copydoc is_equality_comparable */
template<typename Type, auto N>
struct is_equality_comparable<Type[N]>: std::false_type {};
Expand Down
Loading