Sorunun Geçmişi
C++20'den önce, geliştiriciler sıralanabilir türler için altı karşılaştırma operatörünü manuel olarak uygularlardı. Bu şablon, eşitlik ve sıralama ilişkileri arasında ince mantıksal tutarsızlıklar getirebiliyordu. Uzay gemisi operatörü bu karşılaştırmaları tek bir kanonik operasyona birleştirmek için tanıtıldı.
Problem
operator<=> sözdizimini azaltırken, derleyici, a > b'den b < a'yı üretmek için bir dönüş türüne güvenmektedir. Sıralamanın nasıl olduğu (güçlü, zayıf veya kısmi) bilinmediği sürece, derleyici bu yeniden yazmaları güvenli bir şekilde üretemez.
Çözüm
Dönüş türü std::strong_ordering, std::weak_ordering veya std::partial_ordering (veya dolaylı olarak dönüştürülebilir) olmalıdır. Bu standart kategori, derleyicinin ters adayları ve dolaylı eşitlik kontrollerini üretmesine olanak tanır. auto veya özel türlerin döndürülmesi bu sentezi devre dışı bırakır ve manuel asimetrik aşırı yüklemeler gerektirir.
struct Widget { int id; // Doğru: ters aday üretimini etkinleştirir std::strong_ordering operator<=>(const Widget&) const = default; };
Senaryo ve Problem
GPU hızlandırmalı geometriler için bir SpatialIndex geliştirirken, std::set ekleme için katı zayıf sıralama gerektiren bir BoundingBox yapısı gerekti. Kutular, mekansal sorgular için ham koordinat dizileri ile karşılaştırılabilmeliydi.
Çözüm 1: Manuel operatör aşırı yükleme
On iki aşırı yüklemenin uygulanması (altı için BoundingBox, altı için koordinat dizileri) açık bir kontrol sağladı. Ancak, sözdizimini risk eden kopyala-yapıştır hataları ve yeniden yapılandırmalar sırasında tutarlılığı korumak zorlayıcıydı.
Çözüm 2: std::weak_ordering döndüren varsayılan uzay gemisi
Bu, tek bir bildirimden otomatik olarak tüm ilişkisel operatörleri üretti. Açık dönüş türü, derleyicinin koordinat dizilerine karşı ters karşılaştırmaları işlemesine olanak tanıdı. Uygulama, sıfır şablonla istisna güvenliği ve matematiksel tutarlılık sağladı.
Çözüm 3: Auto dönüş
auto operator<=>(const BoundingBox&) const = default kullanmak, ters aday sentezini engelledi. Soldaki bir ham diziyi sağdaki bir BoundingBox ile karşılaştırmak derlemeyi başaramadı. Bu asimetri, mekansal sorgu arayüzünü kırdı.
Karar ve Sonuç
Kesin kutuların eşdeğerliği (kesişen kutular eşit karşılaştırılır) ama matematiksel eşitliği olmadığından, std::weak_ordering ile Çözüm 2'yi seçtik. Bu, standart algoritmalarla sorunsuz bir entegrasyon sağlarken, heterojen koordinat karşılaştırmalarını da destekledi.
Derleyici neden operator=='i operator<=>'den sentezler ve bu ne zaman altoptimaldir?
Derleyici, operator=='yi ((*this <=> other) == 0) olarak üretir. Bu tutarlılık sağlar ama eşitlik kontrolü yaparken tam eleman bazlı karşılaştırma zorlar. operator=='yi açıkça varsayılan yapmak, kısa devre değerlendirmesi sağlar ve ilk farklı üyeye ulaşıldığında anında false döndürür.
Üçüncü taraf olarak operator<=>'yi bir üye olarak tanımlamak asimetrikliği nasıl kırar?
Bir üye operator<=>, aşırı yükleme çözümlemesi sırasında yalnızca sağdaki operanda dolaylı dönüşümlere izin verir. Bu asimetri, MyClass'ın double'dan oluşturulabiliyorsa bile double == MyClass gibi ifadelerin derlenmesini engeller. Gizli bir arkadaş kullanmak, Argüman Bağımlı Arama (ADL) sağlar ve her iki operanda da dolaylı olarak dönüşüm yapma olanağı tanır.
std::compare_three_way'i manuel işaretçi karşılaştırmasından ayıran nedir?
std::compare_three_way, tüm adres alanında, std::nullptr_t dahil olmak üzere işaretçiler için tutarlı bir toplam sıralama sağlar. İlişkisiz nesneler arasında karşılaştırma yapmak, manuel işaretçi karşılaştırmalarında tanımsız davranışa neden olur. Standart işlev nesnesini kullanmak, işaretçi sıralama için taşınabilir, iyi tanımlanmış bir anlam sağlar.