Là == và! = Phụ thuộc lẫn nhau?


292

Tôi đang tìm hiểu về quá tải toán tử trong C ++ và tôi thấy điều đó ==!=chỉ đơn giản là một số chức năng đặc biệt có thể được tùy chỉnh cho các loại do người dùng xác định. Tuy nhiên, mối quan tâm của tôi là tại sao có hai định nghĩa riêng biệt cần thiết? Tôi nghĩ rằng nếu a == blà đúng, thì a != btự động là sai, và ngược lại, và không có khả năng nào khác, bởi vì, theo định nghĩa, a != b!(a == b). Và tôi không thể tưởng tượng bất kỳ tình huống nào trong đó không đúng. Nhưng có lẽ trí tưởng tượng của tôi bị hạn chế hoặc tôi không biết gì?

Tôi biết rằng tôi có thể định nghĩa cái này theo nghĩa khác, nhưng đây không phải là điều tôi đang hỏi. Tôi cũng không hỏi về sự khác biệt giữa so sánh các đối tượng theo giá trị hoặc theo danh tính. Hoặc liệu hai đối tượng có thể bằng nhau và không bằng nhau cùng một lúc hay không (đây chắc chắn không phải là một lựa chọn! Những thứ này là loại trừ lẫn nhau). Những gì tôi đang hỏi là đây:

Có bất kỳ tình huống nào có thể trong đó việc đặt câu hỏi về hai đối tượng bằng nhau có ý nghĩa gì không, nhưng hỏi về việc chúng không bằng nhau không có ý nghĩa gì? (theo quan điểm của người dùng hoặc quan điểm của người thực hiện)

Nếu không có khả năng như vậy, thì tại sao trên Trái đất, C ++ lại có hai toán tử này được định nghĩa là hai hàm riêng biệt?


13
Hai con trỏ có thể là cả hai nhưng không nhất thiết phải bằng nhau.
Ali Caglayan

2
Không chắc nó có ý nghĩa ở đây không, nhưng đọc nó làm tôi nghĩ đến các vấn đề 'ngắn mạch'. Ví dụ, người ta có thể định nghĩa 'undefined' != expressionluôn luôn đúng (hoặc sai hoặc không xác định), bất kể biểu thức có thể được đánh giá hay không. Trong trường hợp này a!=bsẽ trả về kết quả chính xác theo định nghĩa, nhưng !(a==b)sẽ thất bại nếu bkhông thể đánh giá được. (Hoặc mất nhiều thời gian nếu đánh giá blà tốn kém).
Dennis Jaheruddin

2
Thế còn null! = Null và null == null? Nó có thể là cả ... vì vậy nếu a! = B không phải lúc nào cũng có nghĩa là a == b.
zozo

4
Một ví dụ từ javascript(NaN != NaN) == true
chiliNUT

Câu trả lời:


272

Bạn sẽ không muốn ngôn ngữ tự động viết lại a != bnhư !(a == b)khi a == btrả về một thứ khác ngoàibool . Và có một vài lý do tại sao bạn có thể làm cho nó làm điều đó.

Bạn có thể có các đối tượng trình tạo biểu thức, trong a == bđó không và không có ý định thực hiện bất kỳ so sánh nào, nhưng chỉ cần xây dựng một số nút biểu thức đại diệna == b .

Bạn có thể lười đánh giá, trong a == bđó không và không có ý định thực hiện bất kỳ so sánh trực tiếp nào, nhưng thay vào đó trả về một số loại lazy<bool>có thể được chuyển đổi thành boolngầm hoặc rõ ràng vào một lúc nào đó để thực sự so sánh. Có thể kết hợp với các đối tượng xây dựng biểu thức để cho phép tối ưu hóa biểu thức hoàn chỉnh trước khi đánh giá.

Bạn có thể có một số optional<T>lớp mẫu tùy chỉnh , trong đó đưa ra các biến tùy chọn tu, bạn muốn cho phép t == u, nhưng làm cho nó trở lạioptional<bool> .

Có lẽ tôi còn nghĩ nhiều hơn thế. Và mặc dù trong các ví dụ này, hoạt động a == ba != bcả hai đều có ý nghĩa, nhưng vẫn a != bkhông giống như !(a == b)vậy, vì vậy cần có các định nghĩa riêng biệt.


72
Xây dựng biểu thức là một ví dụ thực tế tuyệt vời khi bạn muốn điều này, điều này không dựa trên các kịch bản có sẵn.
Oliver Charlesworth

6
Một ví dụ tốt khác sẽ là các hoạt động logic vector. Bạn thà một đường chuyền qua các dữ liệu máy tính !=thay vì hai đèo tính toán ==sau đó !. Đặc biệt là vào thời mà bạn không thể dựa vào trình biên dịch để hợp nhất các vòng lặp. Hoặc thậm chí ngay hôm nay nếu bạn không thuyết phục được trình biên dịch, vectơ của bạn không trùng nhau.

41
"Bạn có thể có biểu hiện xây dựng các đối tượng" - cũng sau đó điều hành !cũng có thể xây dựng một số nút biểu hiện và chúng ta vẫn sử dụng tốt thay thế a != bvới !(a == b), xa như vậy mà đi. Cùng đi lazy<bool>::operator!, nó có thể trở lại lazy<bool>. optional<bool>là thuyết phục hơn, vì tính trung thực logic của ví dụ boost::optionalphụ thuộc vào việc một giá trị có tồn tại, không phụ thuộc vào chính giá trị đó.
Steve Jessop

42
Tất cả điều đó, và Nans - hãy nhớ NaNs;
jsbueno

9
@jsbueno: người ta chỉ ra rằng NaNs không đặc biệt về vấn đề này.
Oliver Charlesworth

110

Nếu không có khả năng như vậy, thì tại sao trên Trái đất, C ++ lại có hai toán tử này được định nghĩa là hai hàm riêng biệt?

Bởi vì bạn có thể quá tải chúng, và bằng cách quá tải chúng, bạn có thể mang đến cho chúng một ý nghĩa hoàn toàn khác với nghĩa gốc của chúng.

Lấy ví dụ, toán tử <<, ban đầu là toán tử dịch chuyển trái bit, giờ thường bị quá tải như một toán tử chèn, như trong std::cout << something; ý nghĩa hoàn toàn khác với bản gốc.

Vì vậy, nếu bạn chấp nhận rằng ý nghĩa của toán tử thay đổi khi bạn quá tải nó, thì không có lý do gì để ngăn người dùng đưa ra ý nghĩa cho toán tử ==không chính xác là phủ định của toán tử !=, mặc dù điều này có thể gây nhầm lẫn.


18
Đây là câu trả lời duy nhất có ý nghĩa thực tế.
Sonic Atom

2
Đối với tôi có vẻ như bạn có nguyên nhân và kết quả ngược. Bạn có thể quá tải chúng một cách riêng biệt bởi vì ==!=tồn tại như các toán tử riêng biệt. Mặt khác, chúng có thể không tồn tại dưới dạng các toán tử riêng biệt vì bạn có thể quá tải chúng một cách riêng biệt, nhưng vì lý do di sản và tiện lợi (mã ngắn).
nitro2k01

60

Tuy nhiên, mối quan tâm của tôi là tại sao có hai định nghĩa riêng biệt cần thiết?

Bạn không phải xác định cả hai.
Nếu chúng loại trừ lẫn nhau, bạn vẫn có thể súc tích bằng cách chỉ xác định ==<bên cạnh 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';
}

Có bất kỳ tình huống nào có thể trong đó việc đặt câu hỏi về hai đối tượng bằng nhau có ý nghĩa gì không, nhưng hỏi về việc chúng không bằng nhau không có ý nghĩa gì?

Chúng tôi thường liên kết các nhà khai thác để bình đẳng.
Mặc dù đó là cách họ hành xử trên các loại cơ bản, nhưng không có nghĩa vụ rằng đây là hành vi của họ đối với các loại dữ liệu tùy chỉnh. Bạn thậm chí không phải trả lại một bool nếu bạn không muốn.

Tôi đã thấy mọi người quá tải các nhà khai thác theo những cách kỳ quái, chỉ để thấy rằng nó có ý nghĩa đối với ứng dụng cụ thể miền của họ. Ngay cả khi giao diện xuất hiện cho thấy chúng là loại trừ lẫn nhau, tác giả có thể muốn thêm logic bên trong cụ thể.

(theo quan điểm của người dùng hoặc quan điểm của người thực hiện)

Tôi biết bạn muốn có một ví dụ cụ thể,
vì vậy đây là một ví dụ từ khung thử nghiệm Catch mà tôi nghĩ là thiết thực:

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

Các toán tử này đang làm những việc khác nhau và sẽ không có nghĩa gì khi định nghĩa một phương thức này là một (không phải) của phương thức kia. Lý do này được thực hiện, là để khung có thể in ra so sánh được thực hiện. Để làm điều đó, nó cần phải nắm bắt bối cảnh của toán tử quá tải đã được sử dụng.


14
Ôi trời, sao tôi không biết std::rel_ops? Cảm ơn bạn rất nhiều vì đã chỉ ra rằng.
Daniel Jour

5
Các bản sao gần nguyên văn từ cppreference (hoặc bất cứ nơi nào khác) phải được đánh dấu rõ ràng và được quy cho đúng. rel_opsdù sao cũng là kinh khủng
TC

@TC Đồng ý, tôi chỉ nói đó là phương pháp mà OP có thể thực hiện. Tôi không biết làm thế nào để giải thích rel_ops đơn giản hơn ví dụ được hiển thị. Tôi đã liên kết với vị trí của nó, nhưng mã được đăng vì trang tham chiếu luôn có thể thay đổi.
Trevor Hickey

4
Bạn vẫn cần phải làm rõ rằng ví dụ mã là 99% từ cppreference, chứ không phải của riêng bạn.
TC

2
Std :: relops dường như đã hết ưu ái. Kiểm tra ops boost cho một cái gì đó nhắm mục tiêu hơn.
JDługosz

43

Có một số công ước rất tốt được thành lập trong đó (a == b)(a != b)cả hai giả không nhất thiết đối lập. Cụ thể, trong SQL, mọi so sánh với NULL đều mang lại NULL, không đúng hoặc sai.

Có lẽ không nên tạo ra các ví dụ mới về điều này nếu có thể, bởi vì nó không trực quan, nhưng nếu bạn đang cố gắng mô hình hóa một quy ước hiện có, thật tốt khi có tùy chọn để các nhà khai thác của bạn hành xử "chính xác" cho điều đó bối cảnh.


4
Thực hiện hành vi null giống như SQL trong C ++? Ồ Nhưng tôi cho rằng đó không phải là thứ mà tôi nghĩ nên bị cấm trong ngôn ngữ, tuy nhiên nó có thể gây phiền nhiễu.

1
@ dan1111 Quan trọng hơn, một số hương vị của SQL có thể được mã hóa bằng c ++, vì vậy ngôn ngữ cần hỗ trợ cú pháp của chúng, phải không?
Joe

1
Chỉnh sửa cho tôi nếu tôi sai, tôi chỉ tắt wikipedia ở đây, nhưng không so sánh với giá trị NULL trong SQL return Unknown, không sai? Và không phải là phủ định của Unknown vẫn chưa biết? Vì vậy, nếu logic SQL được mã hóa trong C ++, bạn sẽ không muốn NULL == somethingtrả về Unknown và bạn cũng muốn NULL != somethingtrả về Unknown và bạn muốn !Unknownquay lại Unknown. Và trong trường hợp đó thực hiện operator!=như phủ định operator==vẫn là chính xác.
Benjamin Lindley

1
@Barmar: Được rồi, nhưng điều đó làm cho câu lệnh "SQL NULL hoạt động theo cách này" đúng như thế nào? Nếu chúng tôi giới hạn việc triển khai toán tử so sánh của chúng tôi để trả về booleans, điều đó không có nghĩa là việc triển khai logic SQL với các toán tử này là không thể?
Benjamin Lindley

2
@Barmar: Ồ không, đó không phải là vấn đề. OP đã biết thực tế đó, hoặc câu hỏi này sẽ không tồn tại. Vấn đề là đưa ra một ví dụ trong đó có ý nghĩa 1) để thực hiện một operator==hoặc operator!=, nhưng không phải là 2, hoặc 2) để thực hiện operator!=theo cách khác với phủ định operator==. Và việc triển khai logic SQL cho các giá trị NULL không phải là trường hợp đó.
Benjamin Lindley

23

Tôi sẽ chỉ trả lời phần thứ hai của câu hỏi của bạn, cụ thể là:

Nếu không có khả năng như vậy, thì tại sao trên Trái đất, C ++ lại có hai toán tử này được định nghĩa là hai hàm riêng biệt?

Một lý do tại sao nó có ý nghĩa để cho phép nhà phát triển quá tải cả hai là hiệu suất. Bạn có thể cho phép tối ưu hóa bằng cách thực hiện cả hai ==!=. Sau đó x != ycó thể rẻ hơn!(x == y) là. Một số trình biên dịch có thể tối ưu hóa nó cho bạn, nhưng có lẽ là không, đặc biệt là nếu bạn có các đối tượng phức tạp với nhiều nhánh liên quan.

Ngay cả ở Haskell, nơi các nhà phát triển rất coi trọng luật pháp và các khái niệm toán học, người ta vẫn được phép quá tải cả hai ==/=, như bạn có thể thấy ở đây ( 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'

Điều này có thể được coi là tối ưu hóa vi mô, nhưng nó có thể được bảo hành cho một số trường hợp.


3
Các lớp bao bọc SSE (x86 SIMD) là một ví dụ tuyệt vời về điều này. Có một pcmpeqbhướng dẫn, nhưng không có hướng dẫn so sánh đóng gói nào tạo ra a! = Mask. Vì vậy, nếu bạn không thể đảo ngược logic của bất cứ điều gì sử dụng kết quả, bạn phải sử dụng một hướng dẫn khác để đảo ngược nó. (Sự thật thú vị: Tập lệnh XOP của AMD đã được so sánh đóng gói neq. Thật tệ là Intel đã không áp dụng / gia hạn XOP; có một số hướng dẫn hữu ích trong phần mở rộng ISA sắp chết.)
Peter Cordes

1
Toàn bộ điểm của SIMD ở vị trí đầu tiên là hiệu suất và bạn thường chỉ sử dụng nó theo cách thủ công trong các vòng lặp rất quan trọng đối với sự hoàn hảo nói chung. Lưu một lệnh đơn ( PXORvới tất cả các lệnh để đảo ngược kết quả mặt nạ so sánh) trong một vòng lặp chặt chẽ có thể có vấn đề.
Peter Cordes

Hiệu suất như một lý do là không đáng tin khi chi phí chung là một phủ định logic .
Chúc mừng và hth. - Alf

Nó có thể là nhiều hơn một phủ định logic nếu tính toán x == ychi phí nhiều hơn đáng kể x != y. Việc tính toán cái sau có thể rẻ hơn đáng kể do dự đoán chi nhánh, v.v.
Centril

16

Có bất kỳ tình huống nào có thể trong đó việc đặt câu hỏi về hai đối tượng bằng nhau có ý nghĩa gì không, nhưng hỏi về việc chúng không bằng nhau không có ý nghĩa gì? (theo quan điểm của người dùng hoặc quan điểm của người thực hiện)

Đó là một ý kiến. Có lẽ nó không. Nhưng các nhà thiết kế ngôn ngữ, không phải là người toàn diện, đã quyết định không hạn chế những người có thể đưa ra các tình huống có thể có ý nghĩa (ít nhất là với họ).


13

Để đáp ứng với chỉnh sửa;

Đó là, nếu một số loại có thể có toán tử ==nhưng không phải !=, hoặc ngược lại, và khi nào thì có ý nghĩa để làm như vậy.

Nói chung , không, nó không có ý nghĩa. Các toán tử bình đẳng và quan hệ thường đi theo bộ. Nếu có sự bình đẳng, thì bất bình đẳng là tốt; ít hơn, sau đó lớn hơn và như vậy với<= v.v. Một cách tiếp cận tương tự cũng được áp dụng cho các toán tử số học, chúng cũng thường đi kèm trong các tập hợp logic tự nhiên.

Điều này được chứng minh trong std::rel_ops không gian tên. Nếu bạn thực hiện sự bình đẳng và ít hơn các toán tử, sử dụng không gian tên đó mang lại cho bạn các toán tử khác, được triển khai theo các toán tử được triển khai ban đầu của bạn.

Tất cả đã nói, có những điều kiện hoặc tình huống mà cái này không có nghĩa là cái kia ngay lập tức, hoặc không thể được thực hiện theo nghĩa của những cái khác? Vâng, có rất ít, nhưng chúng ở đó; một lần nữa, như đã chứng minh trongrel_ops là không gian tên của chính nó. Vì lý do đó, cho phép chúng được triển khai độc lập cho phép bạn tận dụng ngôn ngữ để có được ngữ nghĩa mà bạn yêu cầu hoặc cần theo cách vẫn tự nhiên và trực quan cho người dùng hoặc khách hàng của mã.

Đánh giá lười biếng đã được đề cập là một ví dụ tuyệt vời về điều này. Một ví dụ điển hình khác là cung cấp cho họ ngữ nghĩa không có nghĩa là bình đẳng hoặc không bình đẳng. Một ví dụ tương tự như vậy là các toán tử dịch chuyển bit <<>>được sử dụng để chèn và trích xuất luồng. Mặc dù nó có thể được tán thành trong các vòng tròn nói chung, trong một số lĩnh vực cụ thể của miền, nó có thể có ý nghĩa.


12

Nếu các toán tử ==!=toán tử không thực sự ngụ ý sự bình đẳng, theo cùng một cách mà các toán tử luồng <<>>luồng không ngụ ý dịch chuyển bit. Nếu bạn coi các biểu tượng như thể chúng có nghĩa là một số khái niệm khác, thì chúng không phải loại trừ lẫn nhau.

Về mặt bình đẳng, sẽ có ý nghĩa nếu trường hợp sử dụng của bạn đảm bảo coi các đối tượng là không thể so sánh, do đó mọi so sánh sẽ trả về sai (hoặc loại kết quả không thể so sánh, nếu toán tử của bạn trả về giá trị không phải là bool). Tôi không thể nghĩ đến một tình huống cụ thể nơi điều này sẽ được bảo hành, nhưng tôi có thể thấy nó đủ hợp lý.


7

Với sức mạnh tuyệt vời đến rất có trách nhiệm, hoặc ít nhất là hướng dẫn phong cách thực sự tốt.

==!=có thể bị quá tải để làm bất cứ điều gì bạn muốn. Đó là cả một phước lành và một lời nguyền. Không có gì đảm bảo điều đó !=có nghĩa !(a==b).


6
enum BoolPlus {
    kFalse = 0,
    kTrue = 1,
    kFileNotFound = -1
}

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

Tôi không thể biện minh cho việc quá tải toán tử này, nhưng trong ví dụ trên, không thể định nghĩa operator!=là "đối diện" của operator==.



1
@Snowman: Dafang không nói đó là một phép liệt kê tốt (cũng không phải là một ý tưởng tốt để định nghĩa một phép liệt kê như vậy), nó chỉ là một ví dụ để minh họa một điểm. Với định nghĩa toán tử (có lẽ là xấu) này, thì !=thực sự sẽ không có nghĩa ngược lại ==.
AlainD

1
@AlainD bạn đã nhấp vào liên kết tôi đã đăng và bạn có biết mục đích của trang web đó không? Điều này được gọi là "hài hước."

1
@Snowman: Tôi chắc chắn làm ... xin lỗi, tôi đã bỏ lỡ nó là một liên kết và có ý định trớ trêu! : o)
AlainD

Chờ đã, bạn đang quá tải unary ==?
LF

5

Cuối cùng, điều bạn đang kiểm tra với các toán tử đó là biểu thức a == b hoặc a != bđang trả về giá trị Boolean ( truehoặc false). Các biểu thức này trả về giá trị Boolean sau khi so sánh thay vì loại trừ lẫn nhau.


4

[..] tại sao có hai định nghĩa riêng biệt cần thiết?

Một điều cần xem xét là có thể có khả năng thực hiện một trong những toán tử này hiệu quả hơn là chỉ sử dụng phủ định của cái kia.

(Ví dụ của tôi ở đây là rác rưởi, nhưng vấn đề vẫn còn tồn tại, hãy nghĩ đến các bộ lọc nở, ví dụ: Chúng cho phép thử nghiệm nhanh nếu có thứ gì đó không có trong một bộ, nhưng thử nghiệm nếu nó có thể mất nhiều thời gian hơn.)

[..] theo định nghĩa, a != b!(a == b).

Và trách nhiệm của bạn là lập trình viên để thực hiện điều đó. Có lẽ là một điều tốt để viết một bài kiểm tra cho.


4
Làm thế nào !((a == rhs.a) && (b == rhs.b))không cho phép ngắn mạch? nếu !(a == rhs.a), sau đó (b == rhs.b)sẽ không được đánh giá.
Benjamin Lindley

Đây là một ví dụ xấu, mặc dù. Việc ngắn mạch thêm không có lợi thế kỳ diệu ở đây.
Oliver Charlesworth

@Oliver Charlesworth Một mình thì không, nhưng khi tham gia với các toán tử riêng biệt, nó sẽ: Trong trường hợp ==, nó sẽ ngừng so sánh ngay khi các phần tử tương ứng đầu tiên không bằng nhau. Nhưng trong trường hợp !=, nếu nó được triển khai theo các khía cạnh ==, thì trước tiên cần phải so sánh tất cả các yếu tố tương ứng (khi tất cả đều bằng nhau) để có thể nói rằng chúng không bằng nhau: P Nhưng khi được triển khai như trong ví dụ trên, nó sẽ ngừng so sánh ngay khi tìm thấy cặp không bằng nhau đầu tiên. Ví dụ tuyệt vời thực sự.
BarbaraKwarc

@BenjaminLindley Đúng, ví dụ của tôi hoàn toàn vô nghĩa. Thật không may, tôi không thể đến với một máy rút tiền khác, ở đây đã quá muộn.
Daniel Jour

1
@BarbaraKwarc: !((a == b) && (c == d))(a != b) || (c != d)tương đương về hiệu quả ngắn mạch.
Oliver Charlesworth

2

Bằng cách tùy chỉnh hành vi của các nhà khai thác, bạn có thể khiến họ làm những gì bạn muốn.

Bạn có thể muốn tùy chỉnh mọi thứ. Ví dụ, bạn có thể muốn tùy chỉnh một lớp. Các đối tượng của lớp này có thể được so sánh chỉ bằng cách kiểm tra một thuộc tính cụ thể. Biết rằng đây là trường hợp, bạn có thể viết một số mã cụ thể chỉ kiểm tra những thứ tối thiểu, thay vì kiểm tra từng bit của mỗi thuộc tính trong toàn bộ đối tượng.

Hãy tưởng tượng một trường hợp mà bạn có thể nhận ra rằng một cái gì đó khác biệt cũng nhanh như vậy, nếu không nhanh hơn, bạn có thể tìm ra thứ gì đó giống nhau. Cấp, một khi bạn tìm ra liệu một cái gì đó giống hay khác nhau, thì bạn có thể biết điều ngược lại chỉ bằng cách lật một chút. Tuy nhiên, lật bit đó là một hoạt động bổ sung. Trong một số trường hợp, khi mã được thực thi lại rất nhiều, việc lưu một thao tác (nhân với nhiều lần) có thể có tốc độ tăng tổng thể. (Ví dụ: nếu bạn lưu một thao tác trên mỗi pixel của màn hình megapixel, thì bạn vừa lưu một triệu thao tác. Nhân với 60 màn hình mỗi giây và bạn còn tiết kiệm được nhiều thao tác hơn nữa.)

câu trả lời của hvd cung cấp một số ví dụ bổ sung.


2

Có, bởi vì một nghĩa là "tương đương" và một nghĩa khác là "không tương đương" và các thuật ngữ này loại trừ lẫn nhau. Bất kỳ ý nghĩa nào khác cho toán tử này là khó hiểu và nên tránh bằng mọi cách.


Họ không loại trừ lẫn nhau cho tất cả các trường hợp. Chẳng hạn, hai vô số cả hai không bằng nhau và không bằng nhau.
vladon

@vladon có thể sử dụng một thay vì khác trong trường hợp chung ? Không. Điều này có nghĩa là họ không bằng nhau. Tất cả phần còn lại chuyển đến một chức năng đặc biệt thay vì toán tử == /! =
oliora

@vladon xin vui lòng, thay vì trường hợp chung đọc tất cả các trường hợp trong câu trả lời của tôi.
oliora

@vladon Nhiều như điều này đúng trong toán học, bạn có thể đưa ra một ví dụ a != bkhông bằng !(a == b)lý do này trong C không?
nitro2k01

2

Có lẽ một quy tắc uncomparable, nơi a != bđã saia == bđã sai như một chút không quốc tịch.

if( !(a == b || a != b) ){
    // Stateless
}

Nếu bạn muốn sắp xếp lại các ký hiệu logic thì! ([A] || [B]) sẽ trở thành ([! A] & [! B])
Thijser

Lưu ý rằng loại trả về operator==()operator!=()không nhất thiết bool, chúng có thể là một enum bao gồm trạng thái không trạng thái nếu bạn muốn điều đó và các toán tử vẫn có thể được xác định để (a != b) == !(a==b)giữ ..
lorro
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.