C ++ 20 đã giới thiệu các phép so sánh mặc định, hay còn gọi là "tàu vũ trụ"operator<=>
, cho phép bạn yêu cầu các toán tử <
/ <=
/ ==
/ !=
/ >=
/ và / hoặc do trình biên dịch tạo ra >
với việc triển khai rõ ràng / ngây thơ (?) ...
auto operator<=>(const MyClass&) const = default;
... nhưng bạn có thể tùy chỉnh điều đó cho các tình huống phức tạp hơn (thảo luận bên dưới). Xem tại đây để biết đề xuất ngôn ngữ, trong đó có các biện minh và thảo luận. Câu trả lời này vẫn phù hợp với C ++ 17 trở về trước và để biết rõ khi nào bạn nên tùy chỉnh việc triển khai operator<=>
....
Có vẻ hơi vô ích khi C ++ chưa Chuẩn hóa điều này sớm hơn, nhưng thường cấu trúc / lớp có một số thành viên dữ liệu để loại trừ khỏi so sánh (ví dụ: bộ đếm, kết quả được lưu trong bộ nhớ cache, dung lượng vùng chứa, thành công của hoạt động cuối cùng / mã lỗi, con trỏ), như cũng như các quyết định cần đưa ra về vô số điều bao gồm nhưng không giới hạn ở:
- trường nào cần so sánh trước, ví dụ: so sánh một
int
thành viên cụ thể có thể loại bỏ rất nhanh 99% các đối tượng không bằng nhau, trong khi một map<string,string>
thành viên thường có các mục giống nhau và tương đối đắt để so sánh - nếu các giá trị được tải trong thời gian chạy, lập trình viên có thể hiểu rõ về trình biên dịch không thể
- trong việc so sánh các chuỗi: phân biệt chữ hoa chữ thường, sự tương đương của khoảng trắng và dấu phân cách, thoát quy ước ...
- độ chính xác khi so sánh phao / đôi
- liệu các giá trị dấu phẩy động NaN có được coi là bằng nhau hay không
- so sánh con trỏ hoặc trỏ đến dữ liệu (và nếu là con trỏ, làm thế nào để biết liệu con trỏ có đến mảng hay không và có bao nhiêu đối tượng / byte cần so sánh)
- cho dù vấn đề trật tự khi so sánh container không được phân loại (ví dụ
vector
, list
), và nếu như vậy cho dù đó là ok để sắp xếp chúng tại chỗ trước khi so sánh vs sử dụng thêm bộ nhớ để temporaries loại mỗi lần một so sánh được thực hiện
- có bao nhiêu phần tử mảng hiện đang giữ các giá trị hợp lệ cần được so sánh (có kích thước ở đâu đó hay là một trạm gác không?)
- thành viên nào của a
union
để so sánh
- chuẩn hóa: ví dụ: các loại ngày có thể cho phép ngày trong tháng hoặc tháng trong năm nằm ngoài phạm vi hoặc một đối tượng hợp lý / phân số có thể có 6/8 trong khi đối tượng khác có 3/4, vì lý do hiệu suất, họ sửa lười biếng với một bước chuẩn hóa riêng biệt; bạn có thể phải quyết định xem có nên kích hoạt chuẩn hóa hay không trước khi so sánh
- phải làm gì khi con trỏ yếu không hợp lệ
- cách xử lý các thành viên và cơ sở không
operator==
tự triển khai (nhưng có thể có compare()
hoặc operator<
hoặc str()
hoặc có ...)
- những gì khóa phải được thực hiện trong khi đọc / so sánh dữ liệu mà các chuỗi khác có thể muốn cập nhật
Vì vậy, thật tuyệt khi có lỗi cho đến khi bạn nghĩ rõ ràng về ý nghĩa của so sánh đối với cấu trúc cụ thể của bạn, thay vì để nó biên dịch nhưng không cung cấp cho bạn kết quả có ý nghĩa tại thời điểm chạy .
Tất cả những gì đã nói, sẽ rất tốt nếu C ++ cho phép bạn nói bool operator==() const = default;
khi bạn quyết định một ==
bài kiểm tra từng thành viên "ngây thơ" là ổn. Tương tự cho !=
. Với nhiều thành viên / cơ sở, "mặc định" <
, <=
, >
, và >=
triển khai dường như vô vọng mặc dù - tầng trên cơ sở thứ tự của lời tuyên bố có thể nhưng rất khó có khả năng được những gì mong muốn, cho mâu thuẫn mệnh lệnh cho đặt hàng thành viên (căn cứ là nhất thiết trước khi các thành viên, nhóm bởi khả năng tiếp cận, xây dựng / phá hủy trước khi sử dụng phụ thuộc). Để trở nên hữu ích hơn, C ++ sẽ cần một hệ thống chú thích thành viên / cơ sở dữ liệu mới để hướng dẫn các lựa chọn - đó sẽ là một điều tuyệt vời để có trong Tiêu chuẩn, lý tưởng là cùng với việc tạo mã dựa trên AST do người dùng xác định ... Tôi mong đợi nó '
Thực hiện điển hình của các toán tử bình đẳng
Một triển khai hợp lý
Có thể một cách triển khai hợp lý và hiệu quả sẽ là:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int == rhs.an_int;
}
Lưu ý rằng điều này cần một operator==
cho MyStruct2
quá.
Ý nghĩa của việc triển khai này và các lựa chọn thay thế, được thảo luận trong tiêu đề Thảo luận về các chi tiết cụ thể của MyStruct1 của bạn bên dưới.
Một cách tiếp cận nhất quán đối với ==, <,> <= vv
Thật dễ dàng sử dụng std::tuple
các toán tử so sánh để so sánh các cá thể lớp của riêng bạn - chỉ cần sử dụng std::tie
để tạo các bộ tham chiếu đến các trường theo thứ tự so sánh mong muốn. Tổng quát hóa ví dụ của tôi từ đây :
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) ==
std::tie(rhs.my_struct2, rhs.an_int);
}
inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) <
std::tie(rhs.my_struct2, rhs.an_int);
}
// ...etc...
Khi bạn "sở hữu" (nghĩa là có thể chỉnh sửa, một hệ số với lib của công ty và bên thứ 3) lớp bạn muốn so sánh, và đặc biệt là với sự sẵn sàng của C ++ 14 để suy ra kiểu trả về hàm từ return
câu lệnh, việc thêm một " tie "chức năng thành viên với lớp bạn muốn để có thể so sánh:
auto tie() const { return std::tie(my_struct1, an_int); }
Sau đó, các so sánh ở trên đơn giản hóa thành:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.tie() == rhs.tie();
}
Nếu bạn muốn có một tập hợp đầy đủ hơn các toán tử so sánh, tôi đề xuất các toán tử tăng cường (tìm kiếm less_than_comparable
). Nếu nó không phù hợp vì lý do nào đó, bạn có thể thích hoặc không thích ý tưởng hỗ trợ macro (trực tuyến) :
#define TIED_OP(STRUCT, OP, GET_FIELDS) \
inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
{ \
return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
}
#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
TIED_OP(STRUCT, ==, GET_FIELDS) \
TIED_OP(STRUCT, !=, GET_FIELDS) \
TIED_OP(STRUCT, <, GET_FIELDS) \
TIED_OP(STRUCT, <=, GET_FIELDS) \
TIED_OP(STRUCT, >=, GET_FIELDS) \
TIED_OP(STRUCT, >, GET_FIELDS)
... sau đó có thể được sử dụng ...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(Phiên bản C ++ 14 thành viên ràng buộc tại đây )
Thảo luận về các chi tiết cụ thể của MyStruct1 của bạn
Có những tác động đối với lựa chọn cung cấp một thành viên độc lập so với operator==()
...
Triển khai tự do
Bạn có một quyết định thú vị để thực hiện. Vì lớp của bạn có thể được xây dựng ngầm từ một MyStruct2
, một hàm độc lập / không phải thành viên bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
sẽ hỗ trợ ...
my_MyStruct2 == my_MyStruct1
... bằng cách đầu tiên tạo một MyStruct1
từ tạm thời my_myStruct2
, sau đó thực hiện so sánh. Điều này chắc chắn sẽ MyStruct1::an_int
được đặt thành giá trị tham số mặc định của hàm tạo là -1
. Tùy thuộc vào việc bạn bao gồm an_int
so sánh trong việc thực hiện của bạn operator==
, một MyStruct1
sức mạnh hoặc có thể không so sánh tương đương với một MyStruct2
mà bản thân so sánh tương đương với MyStruct1
của my_struct_2
thành viên! Hơn nữa, tạo một thành viên tạm thời MyStruct1
có thể là một hoạt động rất kém hiệu quả, vì nó liên quan đến việc sao chép my_struct2
thành viên hiện có thành một thành viên tạm thời, chỉ để vứt bỏ nó sau khi so sánh. (Tất nhiên, bạn có thể ngăn chặn cấu trúc ngầm định này của MyStruct1
s để so sánh bằng cách tạo hàm tạo đó explicit
hoặc loại bỏ giá trị mặc định cho an_int
.)
Thành viên thực hiện
Nếu bạn muốn tránh cấu trúc ngầm của a MyStruct1
từ a MyStruct2
, hãy đặt toán tử so sánh thành một hàm thành viên:
struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie(); // or another approach as above
}
};
Lưu ý rằng const
từ khóa - chỉ cần thiết cho việc triển khai thành viên - khuyên trình biên dịch rằng việc so sánh các đối tượng không sửa đổi chúng, vì vậy có thể được phép trên const
các đối tượng.
So sánh các biểu diễn hiển thị
Đôi khi cách dễ nhất để có được kiểu so sánh bạn muốn có thể là ...
return lhs.to_string() == rhs.to_string();
... mà thường cũng rất đắt - những thứ đó string
được tạo ra một cách đau đớn chỉ để vứt bỏ! Đối với các loại có giá trị dấu phẩy động, so sánh các biểu diễn hiển thị có nghĩa là số chữ số được hiển thị xác định dung sai trong đó các giá trị gần bằng nhau được coi là bằng nhau trong quá trình so sánh.
struct
s của mình để bình đẳng? Và nếu bạn muốn một cách đơn giản, luônmemcmp
có khoảng thời gian dài như vậy cấu trúc của bạn không chứa con trỏ.