Перегрузка оператора SFINAE - PullRequest
0 голосов
/ 07 апреля 2020

Мне трудно понять, почему следующий код не компилируется. Фрагмент содержит два оператора и перегрузки, один для некоторого настраиваемого класса 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

1 Ответ

1 голос
/ 07 апреля 2020

На самом деле ADL проблема:

либо,

  • add using:

    void foo() {
      using ::operator&;
    
      io::E e{io::E::A};
      auto const has_a{e & io::E::A};
    }
    

    Демо

  • или переместите свой первый operator& в namespace io. Демо

...