Мне трудно понять, почему следующий код не компилируется. Фрагмент содержит два оператора и перегрузки, один для некоторого настраиваемого класса enum из заголовка библиотеки и один из некоторого кода приложения. Обе перегрузки используют std :: enable_if для ограничения передаваемых типов, и я не вижу, где может быть какая-то двусмысленность? Когда я удаляю второй оператор и перегружаю код, код компилируется.
#include <cstdint>
#include <type_traits>
// Boilerplate for enum class type trait
namespace detail {
template <typename, typename = void>
struct is_enum_class_impl : std::false_type {};
template <typename T>
struct is_enum_class_impl<T, decltype((void)+T{})> : std::true_type {};
} // namespace detail
// Enum class type trait
template <typename T>
using is_enum_class = std::integral_constant<
bool, !detail::is_enum_class_impl<std::remove_cvref_t<T>>::value &&
std::is_enum<std::remove_cvref_t<T>>::value>;
template <typename T>
inline constexpr bool is_enum_class_v{is_enum_class<T>::value};
template <typename T>
struct is_enum_class_flag : std::false_type {};
template <typename T>
inline constexpr bool is_enum_class_flag_v{is_enum_class_flag<T>::value};
// First operator& overload for enum class
template <typename T>
constexpr auto operator&(T lhs, T rhs)
-> std::enable_if_t<is_enum_class_v<T> && is_enum_class_flag_v<T>, T> {
return static_cast<T>(static_cast<std::underlying_type_t<T>>(lhs) &
static_cast<std::underlying_type_t<T>>(rhs));
}
namespace io {
// Enum class type with user enabled operator overload
enum class E { A = 1u << 0u, B = 1u << 1u, C = 1u << 2u };
} // namespace io
// Specialization to enable operator overload
template <>
struct is_enum_class_flag<io::E> : std::true_type {};
namespace io {
// Some user type plus trait
struct Mask{};
template <typename T>
using is_mask = std::is_same<std::remove_cvref_t<T>, Mask>;
template <typename T>
inline constexpr bool is_mask_v{is_mask<T>::value};
// Another user type plus trait
struct State{};
template <typename T>
using is_state = std::is_same<std::remove_cvref_t<T>, State>;
template <typename T>
inline constexpr bool is_state_v{is_state<T>::value};
template <typename T>
using is_mask_or_state = std::disjunction<is_mask<T>, is_state<T>>;
template <typename T>
inline constexpr bool is_mask_or_state_v{is_mask_or_state<T>::value};
// Second operator& overload for user types
template <typename T, typename U>
constexpr auto operator&(T lhs, U rhs) -> std::enable_if_t<
std::conjunction_v<is_mask_or_state<T>, is_mask_or_state<U>>> {
using R = std::conditional_t<is_mask_v<T> && is_mask_v<U>, Mask, State>;
return static_cast<R>(lhs.value() & rhs.value());
}
// Function fails to compile
void foo() {
io::E e{io::E::A};
// Compiler complains about invalid operands to binary expression ('io::E' and 'io::E') ?
auto const has_a{e & io::E::A};
}
} // namespace io
Вот также ссылка на код на Godbolt: https://godbolt.org/z/nezztu