Итак, я хочу написать автоматический !=:
template<typename U, typename T>
bool operator!=(U&& u, T&& t) {
  return !( std::forward<U>(u) == std::forward<T>(t) );
}
но это невежливо 1. Поэтому я пишу
// T() == U() is valid?
template<typename T, typename U, typename=void>
struct can_equal:std::false_type {};
template<typename T, typename U>
struct can_equal<
   T,
   U,
   typename std::enable_if<
      std::is_convertible<
         decltype( std::declval<T>() == std::declval<U>() ),
         bool
      >::value
   >::type
>: std::true_type {};
 который представляет собой класс признаков типов, который говорит: "is t == u допустимый код, который возвращает тип, конвертируемый в bool".
Итак, я улучшаю свой !=:
template<typename U, typename T,
  typename=typename std::enable_if<can_equal<T,U>::value>::type
>
bool operator!=(U&& u, T&& t) {
  return !( std::forward<U>(u) == std::forward<T>(t) );
}
 и теперь это только допустимое переопределение, если == существует. К сожалению, это немного жадный:
struct test {
};
bool operator==(const test&, const test&);
bool operator!=(const test&, const test&);
 так как он будет хватать почти все test() != test(), а не вызывается выше !=. Я думаю, что это нежелательно - я бы скорее назвал явный !=, чем auto-forward, на == и отрицал.
Итак, я пишу этот класс признаков:
template<typename T, typename U,typename=void>
struct can_not_equal // ... basically the same as can_equal, omitted
 который проверяет, действительно ли T != U.
Затем мы увеличиваем != следующим образом:
template<typename U, typename T,
  typename=typename std::enable_if<
    can_equal<T,U>::value
    && !can_not_equal<T,U>::value
  >::type
>
bool operator!=(U&& u, T&& t) {
  return !( std::forward<U>(u) == std::forward<T>(t) );
}
 который, если вы его разобрали, говорит "это предложение ложно" - operator!= существует между T и U iff operator!= не существует между T и U.
Неудивительно, что каждый компилятор я тестировал segfaults, когда его кормили. (clang 3.2, gcc 4.8 4.7.2 intel 13.0.1).  Я подозреваю, что то, что я делаю, является незаконным, но мне бы хотелось увидеть стандартную ссылку. (edit: то, что я делаю, является незаконным, поскольку оно вызывает неограниченное рекурсивное расширение шаблона, если мой != применяется, мы должны проверить, применим ли мой !=. Версия, связанная в комментариях с #if 1, дает разумную ошибку).
Но мой вопрос: есть ли способ убедить моего основанного на SFINAE переопределения игнорировать "себя" при принятии решения о том, должен ли он потерпеть неудачу или нет или каким-то образом избавиться от самореференциальной проблемы? Или понизить приоритет моего operator!= до минимума, чтобы выиграть какой-либо явный !=, даже если это не так хорошо соответствует?
Тот, который не проверяет, что "!= не существует" работает достаточно хорошо, но недостаточно для меня, чтобы быть таким же невежливым, как внедрить его в глобальное пространство имен.
Цель - любой код, который будет компилироваться без моего "волшебного" !=, делает то же самое, как только будет введено мое "волшебное" !=. Если и только если != в противном случае недействителен и bool r = !(a==b) хорошо сформирован, если мой "волшебный" != ударит.
Сноска  1: если вы создаете template<typename U, typename T> bool operator!=(U&& u, T&& t), SFINAE будет думать, что каждая пара типов имеет действительный != между ними. Затем, когда вы пытаетесь на самом деле вызвать !=, он создается и не компилируется. Кроме того, вы топаете в функциях bool operator!=( const foo&, const foo& ), потому что вы лучше сочетаетесь для foo() != foo() и foo a, b; a != b;. Я считаю, что оба эти невежливые.
