Como evitar essa frase é falso em um modelo SFINAE?

Então eu quero escrever um automático!=:

template<typename U, typename T>
bool operator!=(U&& u, T&& t) {
  return !( std::forward<U>(u) == std::forward<T>(t) );
}

mas isso é indelicado1. Então eu escrevo

// 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 {};

que é uma classe de traços do tipo que diz "ét == u código válido que retorna um tipo conversível parabool".

Então eu melhorei meu!=:

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) );
}

e agora só é uma substituição válida se== existe. Infelizmente, é um pouco ganancioso:

struct test {
};
bool operator==(const test&, const test&);
bool operator!=(const test&, const test&);

como vai snarf praticamente todos ostest() != test() em vez do acima!= sendo chamado. Eu acho que isso não é desejado - eu prefiro chamar um!= de auto-forward para== e negar.

Então, eu escrevo esta classe de traços:

template<typename T, typename U,typename=void>
struct can_not_equal // ... basically the same as can_equal, omitted

que testa seT != U é válido.

Nós então aumentamos o!= do seguinte modo:

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) );
}

que, se você analisar, diz "esta frase é falsa" -operator!= existe entreT eU se assimoperator!= não existe entreT eU.

Não é de surpreender que todos os compiladores tenham testado os segfaults quando os alimentaram. (clang 3.2, gcc 4.8 4.7.2 intel 13.0.1).Eu suspeito que o que estou fazendo é ilegal, mas eu adoraria ver a referência padrão. (editar: O que estou fazendo é ilegal, porque induz uma expansão de modelo recursiva ilimitada, determinando se meu!= aplica-se requer que verifiquemos se o meu!= aplica-se. A versão vinculada nos comentários, com#if 1, dá um erro sensato).

Mas a minha pergunta: existe alguma maneira de eu convencer minha substituição baseada na SFINAE a ignorar "ela mesma" ao decidir se ela deve falhar ou não, ou de alguma forma se livrar da questão de auto-referência de alguma forma? Ou abaixe a precedência do meuoperator!= baixo o suficiente para qualquer explícito!= vence, mesmo que não seja tão bom como um jogo?

Aquele que não verifica "!= não existe "funciona razoavelmente bem, mas não o suficiente para eu ser tão indelicado quanto injetá-lo no espaço de nomes global.

O objetivo é qualquer código que compile sem minha "mágica"!= faz exatamente a mesma coisa uma vez minha "mágica"!= é introduzido. Se e apenas se!= é de outra forma inválidoe bool r = !(a==b) está bem formado deve minha "mágica"!= chutar.

Nota de rodapé1: Se você criar umtemplate<typename U, typename T> bool operator!=(U&& u, T&& t), SFINAE vai pensar que cada par de tipos tem um válido!= entre eles. Então, quando você tenta realmente ligar!=, é instanciado e não consegue compilar. Além disso, você pisa embool operator!=( const foo&, const foo& ) funções, porque você é um jogo melhor parafoo() != foo() efoo a, b; a != b;. Eu considero fazer essas duas coisas indelicadas.

questionAnswers(1)

yourAnswerToTheQuestion