Are = = and!= wzajemnie zależne?

Uczę się o przeciążaniu operatorów w C++ i widzę, że == i != są po prostu specjalnymi funkcjami, które można dostosować do typów zdefiniowanych przez użytkownika. Moim zmartwieniem jest jednak to, dlaczego potrzebne są dwie oddzielne definicje? Myślałem, że jeśli {[2] } jest prawdą, to a != b jest automatycznie fałszywe i odwrotnie, i nie ma innej możliwości, ponieważ, z definicji, a != b jest !(a == b). I nie mogłem sobie wyobrazić sytuacji, w której to nie było prawdą. Ale być może mój wyobraźnia jest ograniczona, czy czegoś Nie wiem?

Wiem, że mogę zdefiniować jedno w kategoriach drugiego, ale nie o to pytam. Nie pytam też o rozróżnienie między porównywaniem obiektów według wartości lub tożsamości. Albo czy dwa obiekty mogą być równe i nie równe w tym samym czasie (to zdecydowanie nie wchodzi w grę! te rzeczy wzajemnie się wykluczają). Pytam o to:

Czy jest jakakolwiek sytuacja, w której zadawanie pytań o dwa obiekty równe mają sens, ale pytanie o nie a nie nie ma sensu? (zarówno z perspektywy użytkownika, jak i realizatora)

Jeśli nie ma takiej możliwości, to dlaczego na Ziemi C++ ma te dwa operatory zdefiniowane jako dwie różne funkcje?

Author: Baum mit Augen, 2016-06-14

15 answers

Ty Nie chcesz, aby Język automatycznie przepisywał a != b jako !(a == b), gdy a == b zwróci coś innego niż bool. I jest kilka powodów, dla których możesz to zrobić.

Możesz mieć obiekty expression builder, gdzie a == b nie wykonuje i nie jest przeznaczony do wykonywania porównań, ale po prostu buduje węzeł wyrażenia reprezentujący a == b.

Możesz mieć leniwą ocenę, gdzie a == b nie ma i nie ma na celu bezpośredniego porównania, ale zamiast tego zwraca jakiś rodzaj lazy<bool>, który może być przekształcony do bool w sposób dorozumiany lub jawny w późniejszym czasie, aby faktycznie wykonać porównanie. Możliwe połączenie z obiektami Kreatora wyrażeń w celu umożliwienia pełnej optymalizacji wyrażeń przed ewaluacją.

Możesz mieć niestandardową klasę szablonów optional<T>, gdzie podane opcjonalne zmienne t i u, chcesz zezwolić t == u, ale zwróć optional<bool>.

Jest pewnie więcej, o których nie pomyślałem. I nawet chociaż w tych przykładach obie operacje a == b i a != b mają sens, to jednak a != b nie jest tym samym co !(a == b), więc potrzebne są oddzielne definicje.

 273
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-13 22:41:13

Jeśli nie ma takiej możliwości, to dlaczego na Ziemi C++ ma te dwa operatory zdefiniowane jako dwie różne funkcje?

Ponieważ można je przeciążać, a przeciążając je, można nadać im zupełnie inne znaczenie niż oryginał.

Weźmy na przykład operator <<, pierwotnie operator przesunięcia w lewo, obecnie powszechnie przeciążony jako operator wstawiania, jak w std::cout << something; zupełnie inne znaczenie niż oryginał jeden.

Tak więc, jeśli akceptujesz, że znaczenie operatora zmienia się, gdy go przeciążasz, to nie ma powodu, aby uniemożliwiać użytkownikowi nadanie znaczenia operatorowi ==, które nie jest dokładnie negacją operatora , choć może to być mylące.

 110
Author: shrike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 15:34:00

Moim zmartwieniem jest jednak to, dlaczego potrzebne są dwie oddzielne definicje?

Nie musisz definiować obu.
Jeśli wzajemnie się wykluczają, możesz być zwięzły, definiując tylko == i < obok std:: rel_ops

FOM cppreference:

#include <iostream>
#include <utility>

struct Foo {
    int n;
};

bool operator==(const Foo& lhs, const Foo& rhs)
{
    return lhs.n == rhs.n;
}

bool operator<(const Foo& lhs, const Foo& rhs)
{
    return lhs.n < rhs.n;
}

int main()
{
    Foo f1 = {1};
    Foo f2 = {2};
    using namespace std::rel_ops;

    //all work as you would expect
    std::cout << "not equal:     : " << (f1 != f2) << '\n';
    std::cout << "greater:       : " << (f1 > f2) << '\n';
    std::cout << "less equal:    : " << (f1 <= f2) << '\n';
    std::cout << "greater equal: : " << (f1 >= f2) << '\n';
}

Czy jest jakakolwiek sytuacja, w której zadawanie pytań o dwa obiekty równe mają sens, ale pytając o to, że nie są równe nie czyni sens?

Często łączymy te operatory z równością.
Chociaż tak zachowują się na typach podstawowych, nie ma obowiązku, aby było to ich zachowanie na niestandardowych typach danych. Nie musisz nawet zwracać boola, jeśli nie chcesz.

Widziałem ludzi przeciążających operatorów w dziwaczny sposób, tylko po to, aby odkryć, że ma to sens dla ich konkretnej aplikacji domeny. Nawet jeśli interfejs wydaje się pokazywać, że wzajemnie się wykluczają, autor może chcieć dodaj określoną wewnętrzną logikę.

Z perspektywy użytkownika lub realizatora)

(zarówno z perspektywy użytkownika, jak i realizatora)

Wiem, że chcesz konkretnego przykładu,
oto jeden z Catch testing framework , który moim zdaniem był praktyczny:

template<typename RhsT>
ResultBuilder& operator == ( RhsT const& rhs ) {
    return captureExpression<Internal::IsEqualTo>( rhs );
}

template<typename RhsT>
ResultBuilder& operator != ( RhsT const& rhs ) {
    return captureExpression<Internal::IsNotEqualTo>( rhs );
}

Te operatory robią różne rzeczy i nie miałoby sensu definiowanie jednej metody jako !(Nie) drugiej. Powodem tego jest to, że ramy mogą wydrukować wykonane porównanie. W aby to zrobić, musi uchwycić kontekst tego, co przeciążony operator został użyty.

 60
Author: Trevor Hickey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 02:46:12

Istnieją bardzo ugruntowane konwencje, w których (a == b) i (a != b) zarówno fałszywymi , niekoniecznie przeciwstawnymi. W szczególności, w SQL, każde porównanie z NULL daje NULL, nie prawda lub FAŁSZ.

To prawdopodobnie nie jest dobry pomysł, aby tworzyć nowe przykłady tego, jeśli w ogóle jest to możliwe, ponieważ jest to tak nieintuicyjne, ale jeśli próbujesz modelować istniejącą konwencję, dobrze jest mieć opcję, aby Twoje operatory zachowywały się "poprawnie" w tym kontekście.

 42
Author: Jander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-16 04:09:30

Odpowiem tylko na drugą część twojego pytania, a mianowicie:

Jeśli nie ma takiej możliwości, to dlaczego na Ziemi C++ ma te dwa operatory zdefiniowane jako dwie różne funkcje?

Jednym z powodów, dla których ma sens pozwolić deweloperowi na przeciążenie obu stron, jest wydajność. Możesz zezwolić na optymalizację, implementując zarówno ==, jak i !=. Wtedy {[3] } może być tańsze niż !(x == y) jest. Niektóre Kompilatory mogą być w stanie zoptymalizować go dla ciebie, ale być może nie, zwłaszcza jeśli masz złożone obiekty z dużą ilością rozgałęzień.

Nawet w Haskell, gdzie programiści bardzo poważnie traktują prawa i pojęcia matematyczne, nadal można przeciążać zarówno ==, jak i /=, Jak widać tutaj ( http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html#v:-61--61-):

$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
λ> :i Eq
class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
        -- Defined in `GHC.Classes'
[7]} prawdopodobnie można by to uznać za mikro-optymalizację, ale w niektórych przypadkach może to być uzasadnione.
 23
Author: Centril,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 23:11:00

Czy jest jakakolwiek sytuacja, w której zadawanie pytań o dwa obiekty równe mają sens, ale pytając o to, że nie są równość nie ma sensu? (albo z punktu widzenia użytkownika, albo implementer ' s perspective)

To jest opinia. Może nie. ale projektanci języka, nie będąc wszechwiedzącymi, postanowili nie ograniczać ludzi, którzy mogą wymyślić sytuacje, w których może to mieć sens(przynajmniej dla nich).
 16
Author: Benjamin Lindley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-13 22:30:22

W odpowiedzi na edit;

To znaczy, jeśli jest możliwe, aby jakiś typ miał operator ==, ale nie !=, lub odwrotnie, i kiedy to ma sens.

Ingeneral , nie, to nie ma sensu. Operatory równościowe i relacyjne występują zazwyczaj w zestawach. Jeśli istnieje równość, to również nierówność; mniej niż, potem więcej niż i tak dalej z <= itd. Podobne podejście stosuje się również do operatorów arytmetycznych, zwykle występują w naturalnych zestawach logicznych.

Jest to potwierdzone w std::rel_ops przestrzeń nazw. Jeśli zaimplementujesz operatory równe i mniejsze niż, to użycie tej przestrzeni nazw daje Ci inne, zaimplementowane w kategoriach oryginalnych zaimplementowanych operatorów.

To wszystko powiedziane, Czy istnieją warunki lub sytuacje, w których jeden nie oznaczałby od razu drugiego, lub nie mógłby być wdrożony w kategoriach innych? Tak, są , prawdopodobnie niewiele, ale są tam; ponownie, co zostało udowodnione w rel_ops jako przestrzeni nazw własnej. Z tego powodu, Zezwolenie na ich samodzielne wdrożenie pozwala wykorzystać język, aby uzyskać semantykę wymaganą lub potrzebną w sposób, który jest nadal naturalny i intuicyjny dla użytkownika lub klienta kodu.

Wspomniana już leniwa ocena jest doskonałym tego przykładem. Innym dobrym przykładem jest nadanie im semantyki, która wcale nie oznacza równości ani równości. Podobnym przykładem jest operatory bit shift << i >> używane do wstawiania i ekstrakcji strumienia. Chociaż może to być źle widziane w ogólnych kręgach, w niektórych dziedzinach określonych obszarów może to mieć sens.

 13
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-15 11:15:17

Jeśli operatory == i != nie implikują równości, tak samo jak operatory strumienia << i >> nie implikują przesunięcia bitów. Jeśli traktujesz symbole tak, jakby miały na myśli jakieś inne pojęcie, nie muszą się wzajemnie wykluczać.

Pod względem równości, może to mieć sens, jeśli twój przypadek użycia wymaga traktowania obiektów jako nieporównywalnych, tak że każde porównanie powinno zwracać false (lub nieporównywalny Typ wyniku, jeśli Twoje operatory zwracają non-bool). Nie mogę. pomyśl o konkretnej sytuacji, w której byłoby to uzasadnione, ale widzę, że jest to wystarczająco rozsądne.

 12
Author: Taywee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 16:32:57

Z wielką mocą przychodzi Wielki odpowiedzialnie, a przynajmniej naprawdę dobre Przewodniki stylu.

== i != może być przeciążony, aby zrobić, co chcesz. To błogosławieństwo i przekleństwo. Nie ma gwarancji, że != oznacza !(a==b).

 7
Author: It'sPete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-03 01:13:03
enum BoolPlus {
    kFalse = 0,
    kTrue = 1,
    kFileNotFound = -1
}

BoolPlus operator==(File& other);
BoolPlus operator!=(File& other);

Nie mogę uzasadnić przeciążenia tego operatora, ale w powyższym przykładzie nie można zdefiniować operator!= jako "przeciwieństwa" operator==.

 6
Author: Dafang Cao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-13 22:44:27

Na końcu sprawdzasz, czy wyrażenie a == b LUB a != b Zwraca wartość logiczną (true lub false). Wyrażenie to zwraca wartość logiczną po porównaniu, zamiast wzajemnie się wykluczać.

 5
Author: Anirudh Sohil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-20 09:47:09

[..] dlaczego potrzebne są dwie oddzielne definicje?

Jedną rzeczą do rozważenia jest to, że może istnieć możliwość implementacji jednego z tych operatorów bardziej efektywnie niż tylko za pomocą negacji drugiego.

(mój przykład tutaj był beznadziejny, ale punkt nadal stoi, pomyśl o filtrach bloom, na przykład: pozwalają na szybkie testowanie, jeśli coś jest nie W zestawie, ale testowanie, czy jest w może zająć dużo więcej czasu.)

[..] przez definicja, a != b to !(a == b).

I Twoim obowiązkiem jako programisty jest, aby to utrzymać. To chyba dobra rzecz do napisania testu.
 4
Author: Daniel Jour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-13 22:50:12

Dostosowując zachowanie operatorów, możesz sprawić, by robili to, co chcesz.

Możesz chcieć dostosować rzeczy. Na przykład, możesz chcieć dostosować klasę. Obiekty tej klasy mogą być porównywane przez sprawdzenie określonej właściwości. Wiedząc, że tak jest, możesz napisać jakiś konkretny kod, który sprawdza tylko minimalne rzeczy, zamiast sprawdzać każdy bit każdej właściwości w całym obiekcie.

Wyobraź sobie przypadek, w którym możesz dowiedzieć się że coś jest inne tak samo szybko, jeśli nie szybciej, niż można dowiedzieć się, że coś jest takie samo. Oczywiście, gdy już zorientujesz się, czy coś jest takie samo, czy inne, możesz poznać coś przeciwnego, po prostu rzucając trochę. Jednak przerzucanie tego bitu jest dodatkową operacją. W niektórych przypadkach, gdy kod jest wielokrotnie wykonywany, zapis jednej operacji (pomnożonej przez wiele razy)może mieć ogólny wzrost prędkości. (Na przykład, jeśli zapiszesz jedną operację na piksel ekranu megapikselowego, to właśnie uratowałeś milion operacji. Pomnożony przez 60 ekranów na sekundę, a zaoszczędzisz jeszcze więcej operacji.)

Odpowiedź Hvd zawiera kilka dodatkowych przykładów.

 2
Author: TOOGAM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-18 00:20:42

Tak, ponieważ jeden oznacza "równoważny", a drugi oznacza "nie równoważny" i te terminy wykluczają się wzajemnie. Wszelkie inne znaczenie tego operatora jest mylące i należy unikać wszelkich środków.

 2
Author: oliora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-29 23:01:48

Może nieporównywalna reguła, gdzie a != b było false i a == b było false Jak bezpaństwowy bit.

if( !(a == b || a != b) ){
    // Stateless
}
 2
Author: ToñitoG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-20 09:46:22