Українська
Порівняння, друзі та потоки
Порівняння та категорії впорядкування
operator== відповідає за рівність, а тристороннє порівняння <=> повертає відношення менше, еквівалентне або більше; деякі типи також допускають невпорядкованість. Звичайний власноруч написаний <=> сам не визначає рівність. Для дробу == реалізовано окремо.
std::strong_ordering підходить для звичайних цілих значень із сильною рівністю. weak_ordering дозволяє еквівалентність без повної взаємозамінності, наприклад порівняння тексту без урахування регістру. partial_ordering допускає unordered, як у порівнянні double з NaN. Сильніший результат може перетворюватися на слабший, але не навпаки.
Для operator<=> = default компілятор порівнює бази й поля в порядку оголошення. Це лексикографічний порядок, а не здогад про предметний зміст. Якщо у версії спочатку оголосити patch, результат буде неправильним для очікуваного порядку major/minor/patch. Оголошення defaulted spaceship також може дати відповідний неявний == за мовними правилами.
Компактна числова версія в лабораторному прикладі не є повною реалізацією SemVer: вона не обробляє prerelease і build metadata. Явне обмеження контракту важливіше за привабливу назву. Для реєстру релізів потрібно узгодити формат і правила, перш ніж реалізовувати порівняння.
Дружні функції та приховані друзі
friend надає конкретній функції чи класу доступ до закритих членів. Дружня функція залишається вільною функцією: вона не має this та отримує об’єкти як явні параметри. Саме тому потоковий оператор із лівим операндом ostream природно оформлюється вільною функцією.
Визначення друга всередині класу часто називають прихованим другом (hidden friend). Пошук через типи аргументів знаходить таку операцію для відповідного класу. Це дозволяє тримати допоміжні операції поруч із інваріантом, не відкриваючи внутрішні поля всьому коду.
Дружба не є транзитивною й не означає, що всі знайомі дружнього класу також дістають доступ. Вона не передає право наслідування автоматично. Надання friend слід оцінювати як частину інтерфейсу: функція тепер залежить від внутрішнього подання, тому його зміна може вимагати змін і в цій функції.
Якщо операція може бути коректно реалізована через публічні методи, friend не обов’язковий. Однак десяток штучних getters лише заради одного форматування теж може погіршити інкапсуляцію. Вибір має зменшувати кількість способів порушити інваріант, а не слідувати абсолютній забороні чи абсолютному дозволу дружби.
Потокове введення без часткового стану
operator<< повертає посилання на той самий потік. Це підтримує ланцюжок out << a << b. Він не повинен без потреби завершувати рядок: розташування перенесень вирішує користувач. Також небажано назавжди змінювати flags чи precision стороннього потоку.
operator>> повинен повертати istream& і позначати невдале читання через стан потоку. Зручна стратегія – прочитати аргументи в локальні змінні, перевірити, а тоді присвоїти нове значення об’єкту. Якщо другий компонент комплексного числа відсутній, перший не повинен вже непомітно змінити половину старого об’єкта.
Приклад використовує пару дійсних чисел через пробіл. Це навмисно простий формат, а не розбір довільного запису a+bi. Після помилки ціль лишається незмінною, а потік має failbit. Подальше відновлення потоку та повторний запит є відповідальністю консольного інтерфейсу.
Винятки й failbit не слід змішувати випадково. За звичайної маски винятків setstate лише змінює прапорці; якщо користувач потоку ввімкнув винятки для failbit, він може отримати ios_base::failure. Контракт оператора має залишатися коректним і в цьому випадку.
Присвоєння, стрілка та межі зручності
Оператор присвоєння пов’язаний із володінням ресурсом і вже розглядався в правилі п’яти. Не слід змінювати його звичний зміст заради арифметичної економії: після a = b користувач очікує відповідний стан a, а не додавання чи запис у журнал замість присвоєння.
operator-> використовується класами-вказівниками. Наприклад, unique_ptr надає доступ до керованого об’єкта. Коректна обгортка повинна узгодити час життя, можливість порожнього стану й доступ до ресурсу; сам синтаксис стрілки не вирішує ці задачі.
Перевантажені логічні операції не варто використовувати як заміну звичайного короткого замикання без уважного розуміння правил обчислення операндів. Для навчального класу безпечніше мати явну предметну операцію або explicit operator bool, ніж дивувати користувача нетиповим && чи ||.
Критерій доброго перевантаження – передбачуваність. Прочитайте вираз без знання реалізації, сформулюйте очікуваний результат і перевірте, чи контракт його виконує. Якщо символ потребує довгого пояснення для кожного використання, іменований метод часто кращий.