Więc chcę pisać automatycznego !=
:Jak uniknąć tego zdania w szablonie SFINAE?
template<typename U, typename T>
bool operator!=(U&& u, T&& t) {
return !(std::forward<U>(u) == std::forward<T>(t));
}
ale to niegrzeczne . Więc piszę
// 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 {};
który jest klasa cechy typ, który mówi „to t == u
ważny kod, który zwraca typu cabrio do bool
”.
więc poprawić swój !=
:
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));
}
a teraz to tylko poprawne przesłanianie jeśli ==
istnieje. Niestety, jest to nieco chciwy:
struct test {
};
bool operator==(const test&, const test&);
bool operator!=(const test&, const test&);
jak będzie snarf się prawie każdy test() != test()
zamiast powyższego !=
miano. Myślę, że nie jest to pożądane - wolę nazwać jednoznacznie !=
niż automatyczne przekazywanie do ==
i negować.
Tak, piszę tę klasę cech:
template<typename T, typename U,typename=void>
struct can_not_equal // ... basically the same as can_equal, omitted
który sprawdza czy T != U
jest prawidłowy.
Następnie rozszerz !=
następująco:
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));
}
które jeśli analizować je, mówi: „To zdanie jest fałszywe” - operator!=
istnieje między T
i U
wtw operator!=
nie istnieje między T
i U
.
Nie jest niespodzianką, że każdy kompilator przetestowałem segfaults, gdy karmione to. (clang 3.2, gcc 4.8 4.7.2 intel 13.0.1). Podejrzewam, że to, co robię jest nielegalne, ale chciałbym zobaczyć standardowe odniesienie. (edytuj: To co robię jest nielegalne, ponieważ indukuje nieograniczoną ekspansję szablonów rekursywnych, ponieważ ustalenie, czy moja aplikacja ma zastosowanie !=
, wymaga sprawdzenia, czy mój !=
ma zastosowanie. Wersja powiązana w komentarzach z #if 1
daje sensowny błąd).
Ale moje pytanie: czy istnieje sposób, w jaki mogę przekonać moje przesłonięcie oparte na SFINAE, aby zignorować "samą siebie", decydując, czy powinno zawieść, czy nie, czy jakoś pozbyć się w jakiś sposób problemu samo-referencyjnego? Lub obniżyć pierwszeństwo mojego operator!=
wystarczająco niskie, aby wygrać jednoznaczne !=
, nawet jeśli w przeciwnym razie nie jest tak dobre dopasowanie?
Ten, który nie sprawdza, czy "!=
nie istnieje" działa dość dobrze, ale nie na tyle dobrze, bym był tak niegrzeczny, jak wstrzyknąć go do globalnej przestrzeni nazw.
Celem jest każdy kod, który skompilowałby się bez mojej "magii". !=
robi dokładnie to samo, gdy moja "magia" zostanie wprowadzona w postaci !=
. Jeśli i tylko wtedy, gdy !=
jest w inny sposób nieważny, ibool r = !(a==b)
jest dobrze uformowany, jeśli moja "magia" !=
zostanie wciągnięta.
Przypis : Jeśli utworzyć template<typename U, typename T> bool operator!=(U&& u, T&& t)
, SFINAE pomyśli, że każda para typów posiada ważne !=
między nimi. Następnie, gdy próbujesz wywołać !=
, jest on tworzony i nie można go skompilować. Oprócz tego możesz stompować funkcje bool operator!=(const foo&, const foo&)
, ponieważ lepiej pasujesz do foo() != foo()
i foo a, b; a != b;
. Rozważam robienie obu tych niegrzecznych.
Jestem pewien, że jesteś świadomy, ale 'namespace std :: rel_ops' z' 'ma praktyczną (i naiwną) wersję tego. –
Zamień domyślny argument szablonu dla parametru innego niż typ i podaj gdzieś kod SSCCE, który można łatwo skopiować. – Xeo
+1 tylko za robienie szalonych rzeczy. Rekurencyjne SFINAE ... Wow :-P –