Wie vermeide ich, dass dieser Satz in einer SFINAE-Vorlage falsch ist?

Also möchte ich eine automatische schreiben!=:

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

aber das ist unhöflich1. Also schreibe ich

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

Das ist eine Klasse von Typmerkmalen, die sagt "istt == u Gültiger Code, der einen Typ zurückgibt, in den konvertiert werden kannbool".

Also verbessere ich meine!=:

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

und jetzt ist es nur eine gültige Überschreibung, wenn== existiert. Leider ist es ein bisschen gierig:

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

wie es wird so ziemlich jeder verschnaufentest() != test() eher als das obige!= gerufen werden. Ich denke das ist nicht erwünscht - ich würde eher eine explizite nennen!= als automatische Weiterleitung an== und negieren.

Also schreibe ich diese Traits-Klasse auf:

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

welche prüft obT != U ist gültig.

Wir erweitern dann die!= wie folgt:

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

was, wenn Sie es analysieren, sagt "dieser Satz ist falsch" -operator!= besteht zwischenT undU iffoperator!= existiert nicht zwischenT undU.

Es ist nicht überraschend, dass jeder Compiler, den ich getestet habe, Segfaults anzeigt. (clang 3.2, gcc 4.8 4.7.2 intel 13.0.1).Ich vermute, dass das, was ich tue, illegal ist, aber ich würde gerne die Standardreferenz sehen. (edit: Was ich tue, ist illegal, weil es eine uneingeschränkte rekursive Template - Erweiterung hervorruft, um festzustellen, ob meine!= Dies setzt voraus, dass wir prüfen, ob meine!= gilt. Die in den Kommentaren verlinkte Version mit#if 1gibt einen vernünftigen Fehler aus).

Aber meine Frage: Gibt es eine Möglichkeit, meinen SFINAE-basierten Override davon zu überzeugen, "sich selbst" zu ignorieren, wenn ich entscheide, ob er fehlschlägt oder nicht, oder das selbstbezogene Problem irgendwie loszuwerden? Oder verringern Sie die Priorität von meinemoperator!= niedrig genug, so dass keine explizite!= gewinnt, auch wenn es sonst nicht so gut passt?

Derjenige, der nicht überprüft "!= "existiert nicht" funktioniert einigermaßen gut, aber nicht gut genug, um so unhöflich zu sein, dass ich es in den globalen Namespace einspeisen kann.

Das Ziel ist jeder Code, der ohne meine "Magie" kompiliert werden würde.!= macht genau das gleiche einmal meine "Magie"!= ist vorgestellt. Dann und nur dann, wenn!= ist sonst ungültigund bool r = !(a==b) ist wohlgeformt sollte meine "Magie"!= eintreten.

Fußnote1: Wenn Sie einetemplate<typename U, typename T> bool operator!=(U&& u, T&& t)SFINAE wird denken, dass jedes Paar von Typen eine gültige hat!= zwischen ihnen. Dann, wenn Sie versuchen, tatsächlich anzurufen!=wird instanziiert und kann nicht kompiliert werden. Darüber hinaus stampfen Sie weiterbool operator!=( const foo&, const foo& ) funktioniert, weil Sie besser dazu passenfoo() != foo() undfoo a, b; a != b;. Ich denke darüber nach, beides unhöflich zu machen.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage