Quindi voglio scrivere un automatico !=
:Come evitare questa frase è falsa in un modello SFINAE?
template<typename U, typename T>
bool operator!=(U&& u, T&& t) {
return !(std::forward<U>(u) == std::forward<T>(t));
}
ma che è maleducato . Così scrivo
// 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 {};
che è una classe di tipo tratti che dice "è t == u
codice valido che restituisce un tipo convertibile in bool
".
Così ho migliorare il mio !=
:
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));
}
ed ora è solo una sostituzione valida se ==
esiste. Purtroppo, è un po 'avido:
struct test {
};
bool operator==(const test&, const test&);
bool operator!=(const test&, const test&);
come sarà snarf fino praticamente ogni test() != test()
piuttosto che quanto sopra !=
essere chiamato. Penso che questo non sia desiderato - preferirei chiamare un esplicito !=
piuttosto che l'inoltro automatico a ==
e negare.
Così, scrivo questa classe tratti:
template<typename T, typename U,typename=void>
struct can_not_equal // ... basically the same as can_equal, omitted
che verifica se T != U
è valido.
Abbiamo poi aumentare il !=
come segue:
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));
}
che, se si analizza, dice "questa frase è falsa" - operator!=
esiste tra T
e U
sse operator!=
non esiste tra il T
e U
.
Non sorprendentemente, ogni compilatore ho testato i segfaults quando li ho nutriti. (clang 3.2, gcc 4.8 4.7.2 intel 13.0.1). Sospetto che ciò che sto facendo sia illegale, ma mi piacerebbe vedere il riferimento standard. (modifica: Quello che sto facendo è illegale, perché induce un'espansione ricorsiva illimitata del modello, come determinare se il mio !=
si applica richiede che controlliamo se si applica il mio !=
. La versione collegata nei commenti, con #if 1
, dà un errore ragionevole).
Ma la mia domanda: c'è un modo per convincere la mia esclusione in base SFINAE di ignorare "si" al momento di decidere se dovesse fallire o no, o in qualche modo a sbarazzarsi del problema referenziale sé in qualche modo? O abbassare la precedenza del mio operator!=
abbastanza basso così ogni !=
esplicita vince, anche se non è altrimenti una corrispondenza?
Quello che non verifica la presenza di "!=
non esiste" funziona abbastanza bene, ma non abbastanza bene da essere altrettanto scortese da iniettarlo nello spazio dei nomi globale.
L'obiettivo è qualsiasi codice che possa essere compilato senza la mia "magia" !=
fa esattamente la stessa cosa una volta che viene introdotta la mia "magia" !=
. Se e solo se !=
è altrimenti invalido, ebool r = !(a==b)
è ben formato se il mio "magico" !=
entra.
nota : Se si crea un template<typename U, typename T> bool operator!=(U&& u, T&& t)
, SFINAE penserà che ogni paio di tipi ha una valida !=
tra di loro. Quindi quando si tenta di chiamare effettivamente !=
, viene istanziato e non riesce a compilare. Inoltre, esegui il doppiaggio delle funzioni bool operator!=(const foo&, const foo&)
, perché hai una corrispondenza migliore per foo() != foo()
e foo a, b; a != b;
. Considero di fare entrambi questi scortesi.
Sono sicuro che lo sapete, ma 'namespace std :: rel_ops' da' 'ha la versione pratica (e ingenua) di questo. –
Scambia l'argomento del modello predefinito per un parametro non di tipo e fornisci un SSCCE da qualche parte che possa essere facilmente copiato. – Xeo
+1 solo per fare cose pazzesche. SFINAE ricorsivo ... Wow :-P –