C ++ 20 представив порівняння за замовчуванням, вони ж "космічний корабель"operator<=>
, що дозволяє запитувати згенеровані компілятором <
/ <=
/ ==
/ !=
/ >=
/ та / або >
оператори з очевидною / наївною (?) Реалізацією ...
auto operator<=>(const MyClass&) const = default;
... але ви можете налаштувати це для більш складних ситуацій (обговорено нижче). Дивіться тут мовну пропозицію, яка містить обґрунтування та обговорення. Ця відповідь залишається актуальною для C ++ 17 та попередніх версій, а також для розуміння того, коли слід налаштовувати реалізаціюoperator<=>
....
Може здатися трохи корисним для C ++, якщо він ще не стандартизував це раніше, але часто структури / класи мають деякі члени даних, які слід виключити із порівняння (наприклад, лічильники, кешовані результати, ємність контейнера, код успіху / помилки останньої операції, курсори), як а також рішення щодо безлічі речей, включаючи, але не обмежуючись ними:
- які поля потрібно порівняти спочатку, наприклад, порівняння певного
int
члена може дуже швидко усунути 99% нерівних об’єктів, тоді як map<string,string>
член може часто мати однакові записи та бути порівняно дорогим для порівняння - якщо значення завантажуються під час виконання, програміст може мати уявлення про компілятор не може
- при порівнянні рядків: чутливість до регістру, еквівалентність пробілів та роздільників, уникнення конвенцій ...
- точність при порівнянні плаваючих / подвійних
- чи слід вважати рівними значення NaN з плаваючою комою
- порівняння покажчиків або вказівників на дані (і якщо останні, як дізнатись, чи є покажчики масивами та скільки об’єктів / байтів потребують порівняння)
- чи має значення порядок при порівнянні невідсортованих контейнерів (наприклад
vector
,list
), і якщо так, чи нормально сортувати їх на місці перед порівнянням порівняно з використанням додаткової пам'яті для сортування часописів кожного разу, коли проводиться порівняння
- скільки елементів масиву на даний момент містять допустимі значення, які слід порівнювати (чи є десь розмір чи сторожовий?)
- який член a
union
для порівняння
- нормалізація: наприклад, типи дат можуть дозволяти виходити за межі діапазону день місяця або місяць року, або раціональний / дрібний об’єкт може мати 6/8-ми, тоді як інший має 3/4, які з міркувань ефективності вони виправляють ліниво з окремим кроком нормалізації; можливо, вам доведеться вирішити, чи запускати нормалізацію перед порівнянням
- що робити, коли слабкі вказівники не є дійсними
- як обробляти члени та бази, які не реалізуються
operator==
самі (але можуть мати compare()
або operator<
або str()
або отримувачі ...)
- які блокування потрібно робити під час читання / порівняння даних, які інші потоки можуть захотіти оновити
Отож, приємно мати помилку, поки ви чітко не задумалися над тим, що має означати порівняння для вашої конкретної структури, замість того, щоб дозволити їй компілюватись, але не дати вам значущих результатів під час виконання .
З усього сказаного, було б добре, якби C ++ дозволив вам сказати, bool operator==() const = default;
коли ви вирішили, що "наївний" ==
тест кожного за членом - це нормально. Те саме для !=
. З огляду на кілька членів / баз, « по умовчанням» <
, <=
, >
, і >=
реалізації здаються безнадійними , хоча - каскадні на основі порядку декларації можливо , але навряд чи буде то , що хотів, враховуючи суперечливі імперативи для впорядкування членів (підстави бути обов'язково перед членами, угруповання по доступність, будівництво / руйнування перед залежним використанням). Щоб бути більш корисним, C ++ потребуватиме нової системи даних членів / базових анотацій, щоб керувати вибором - це було б чудово, якщо б це було в Стандарті, хоча в ідеалі в поєднанні з генерацією коду, що визначається користувачем, на основі AST ... Я очікую це '
Типова реалізація операторів рівності
Правдоподібна реалізація
Цілком ймовірно, що розумною та ефективною реалізацією буде:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int == rhs.an_int;
}
Зверніть увагу , що це потребує operator==
протягом MyStruct2
занадто.
Наслідки цієї реалізації та альтернативи обговорюються в розділі Обговорення особливостей вашого MyStruct1 нижче.
Послідовний підхід до ==, <,> <= тощо
Легко використати std::tuple
оператори порівняння для порівняння власних екземплярів класу - просто використовуйте std::tie
для створення кортежів посилань на поля в бажаному порядку порівняння. Узагальнюючи мій приклад звідси :
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...
Коли ви "володієте" (тобто можете редагувати, коефіцієнт з корпоративними і сторонніми бібліотеками) класом, який ви хочете порівняти, і особливо з готовністю C ++ 14 вивести тип повернення функції з return
виписки, часто приємніше додати " прив'язати "функцію члена до класу, який ви хочете мати можливість порівняти:
auto tie() const { return std::tie(my_struct1, an_int); }
Тоді порівняння вище спрощуються до:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.tie() == rhs.tie();
}
Якщо ви хочете отримати більш повний набір операторів порівняння, я пропоную оператори підвищення (шукати less_than_comparable
). Якщо він непридатний з якихось причин, вам може сподобатися чи не сподобатися ідея підтримки макросів (в Інтернеті) :
#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)
... що потім можна використовувати а-ля ...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(C ++ 14 версія для членів тут )
Обговорення особливостей вашого MyStruct1
Вибір має намір забезпечити окремо стоять проти учасника operator==()
...
Автономна реалізація
Вам потрібно прийняти цікаве рішення. Оскільки ваш клас можна неявно побудувати з MyStruct2
, bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
функція, що стоїть окремо / не є членом , підтримує ...
my_MyStruct2 == my_MyStruct1
... спочатку створивши тимчасове MyStruct1
з my_myStruct2
, потім зробивши порівняння. Це однозначно залишило б MyStruct1::an_int
встановленим значення параметра конструктора за замовчуванням -1
. Залежно від того, чи включаєте ви an_int
порівняння у реалізацію вашого operator==
, MyStruct1
можна порівняти чи не порівняти рівне з тим, MyStruct2
яке саме порівнює, рівне члену MyStruct1
' my_struct_2
! Крім того, створення тимчасового MyStruct1
може бути дуже неефективною операцією, оскільки воно передбачає копіювання існуючого my_struct2
члена до тимчасового, лише щоб викинути його після порівняння. (Звичайно, ви можете запобігти цій неявній конструкції MyStruct1
s для порівняння, створивши цей конструктор explicit
або видаливши значення за замовчуванням для an_int
.)
Впровадження членів
Якщо ви хочете уникнути неявної конструкції a MyStruct1
з a MyStruct2
, зробіть оператор порівняння функцією-членом:
struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie(); // or another approach as above
}
};
Зверніть увагу, const
ключове слово - необхідне лише для реалізації члена - радить компілятору, що порівняння об’єктів не змінює їх, тому може бути дозволено для const
об’єктів.
Порівняння видимих уявлень
Іноді найпростішим способом отримати потрібне порівняння може бути ...
return lhs.to_string() == rhs.to_string();
... що часто теж дуже дорого - ті, string
які болісно створені, аби їх викинути! Для типів із значеннями з плаваючою комою порівняння видимих подань означає, що кількість відображуваних цифр визначає допуск, в межах якого майже рівні значення розглядаються як рівні під час порівняння.
struct
s для рівності? І якщо ви хочете простий спосіб, завждиmemcmp
так довго ваші структури не містять покажчика.