Saya penggemar berat membiarkan kompiler melakukan sebanyak mungkin pekerjaan untuk Anda. Saat menulis kelas sederhana kompiler dapat memberi Anda yang berikut ini secara 'gratis':
- Konstruktor (kosong) default
- Pembuat salinan
- Seorang destruktor
- Operator penugasan (
operator=
)
Tapi sepertinya tidak bisa memberi Anda operator perbandingan - seperti operator==
atau operator!=
. Sebagai contoh:
class foo
{
public:
std::string str_;
int n_;
};
foo f1; // Works
foo f2(f1); // Works
foo f3;
f3 = f2; // Works
if (f3 == f2) // Fails
{ }
if (f3 != f2) // Fails
{ }
Apakah ada alasan bagus untuk ini? Mengapa melakukan perbandingan anggota-oleh-anggota menjadi masalah? Tentunya jika kelas mengalokasikan memori maka Anda ingin berhati-hati, tetapi untuk kelas sederhana tentunya kompiler dapat melakukan ini untuk Anda?
==
, dengan cara yang sama bahwa ada tugas otomatis default (=
) dalam kondisi tertentu. (Argumen tentang pointer tidak konsisten karena logika berlaku untuk=
dan==
, dan bukan hanya untuk yang kedua).Jawaban:
Kompiler tidak akan tahu apakah Anda menginginkan perbandingan pointer atau perbandingan (internal) yang mendalam.
Lebih aman untuk tidak mengimplementasikannya dan membiarkan programmer melakukannya sendiri. Kemudian mereka dapat membuat semua asumsi yang mereka sukai.
sumber
operator=
) umumnya bekerja dalam konteks yang sama dengan operator pembanding - yaitu, ada harapan bahwa setelah Anda melakukana = b
,a == b
itu benar. Jelas masuk akal bagi kompiler untuk menyediakan defaultoperator==
menggunakan semantik nilai agregat yang sama seperti halnya untukoperator=
. Saya menduga paercebal sebenarnya benar di sini karenaoperator=
(dan copy ctor) disediakan semata-mata untuk kompatibilitas C, dan mereka tidak ingin memperburuk situasi.Argumen bahwa jika kompiler dapat memberikan konstruktor salinan default, itu harus dapat memberikan default yang sama
operator==()
masuk akal. Saya berpikir bahwa alasan untuk keputusan untuk tidak menyediakan default yang dibuat compiler untuk operator ini dapat ditebak oleh apa yang dikatakan Stroustrup tentang konstruktor copy default di "Desain dan Evolusi C ++" (Bagian 11.4.1 - Kontrol Menyalin) :Jadi alih-alih "mengapa C ++ tidak memiliki default
operator==()
?", Pertanyaannya seharusnya adalah "mengapa C ++ memiliki tugas default dan menyalin konstruktor?", Dengan jawabannya adalah item-item tersebut dimasukkan dengan enggan oleh Stroustrup untuk kompatibilitas dengan C (mungkin penyebab sebagian besar kutil C ++, tetapi juga mungkin alasan utama popularitas C ++).Untuk tujuan saya sendiri, dalam IDE saya cuplikan yang saya gunakan untuk kelas baru berisi deklarasi untuk operator penugasan pribadi dan menyalin konstruktor sehingga ketika saya membuat kelas baru saya tidak mendapatkan tugas default dan menyalin operasi - saya harus secara eksplisit menghapus deklarasi operasi-operasi tersebut dari
private:
bagian jika saya ingin kompiler dapat menghasilkannya untuk saya.sumber
Foo(const Foo&) = delete; // no copy constructor
danFoo& Foo=(const Foo&) = delete; // no assignment operator
struct
, tapi saya berharap itu membiarkannyaclass
berperilaku berbeda (dan secara masuk akal). Dalam prosesnya, itu juga akan memberikan perbedaan yang lebih bermakna antarastruct
danclass
di samping akses standar.operator==
. Pada titik ini hanya sintaksis gula untuk beberapa kode boiler-plate. Jika Anda takut bahwa cara ini programmer dapat mengabaikan beberapa pointer di antara bidang kelas, Anda dapat menambahkan kondisi bahwa itu hanya dapat bekerja pada tipe dan objek primitif yang memiliki operator kesetaraan sendiri. Tidak ada alasan untuk melarang ini sepenuhnya.Bahkan di C ++ 20, kompiler masih tidak akan secara implisit menghasilkan
operator==
untuk AndaTetapi Anda akan mendapatkan kemampuan untuk secara default default
==
sejak C ++ 20 :Defaulting
==
dilakukan oleh anggota==
(dengan cara yang sama seperti pembuat salinan standar mengerjakan konstruksi salinan anggota). Aturan baru juga memberikan hubungan yang diharapkan antara==
dan!=
. Misalnya, dengan deklarasi di atas, saya dapat menulis keduanya:Fitur khusus ini (default
operator==
dan simetri antara==
dan!=
) berasal dari satu proposal yang merupakan bagian dari fitur bahasa yang lebih luasoperator<=>
.sumber
= default
, untuk hal yang tidak dibuat secara default, bukan? Kedengarannya seperti oxymoron bagi saya ("default eksplisit").IMHO, tidak ada alasan "baik". Alasan ada begitu banyak orang yang setuju dengan keputusan desain ini adalah karena mereka tidak belajar untuk menguasai kekuatan semantik berbasis nilai. Orang-orang perlu menulis banyak konstruktor salinan kustom, operator perbandingan dan destruktor karena mereka menggunakan pointer mentah dalam implementasi mereka.
Saat menggunakan smart pointer yang tepat (seperti std :: shared_ptr), konstruktor salinan default biasanya baik dan implementasi jelas dari operator perbandingan default hipotetis akan sama baiknya.
sumber
Itu dijawab C ++ tidak melakukan == karena C tidak, dan inilah mengapa C hanya menyediakan default = tetapi tidak ada == pada awalnya. C ingin tetap sederhana: C diimplementasikan = oleh memcpy; Namun, == tidak dapat diterapkan oleh memcmp karena padding. Karena padding tidak diinisialisasi, memcmp mengatakan mereka berbeda walaupun mereka sama. Masalah yang sama ada untuk kelas kosong: memcmp mengatakan mereka berbeda karena ukuran kelas kosong bukan nol. Dapat dilihat dari atas bahwa implementasi == lebih rumit daripada implementasi = dalam C. Beberapa contoh kode mengenai ini. Koreksi Anda dihargai jika saya salah.
sumber
operator=
- yang hanya akan berfungsi untuk tipe POD, tetapi C ++ juga menyediakan defaultoperator=
untuk tipe non POD.Dalam video ini Alex Stepanov, pencipta STL menjawab pertanyaan ini sekitar pukul 13:00. Sebagai rangkuman, setelah menyaksikan evolusi C ++ ia berpendapat bahwa:
Dia kemudian mengatakan bahwa di masa depan (jauh) == dan ! = Akan dihasilkan secara implisit.
sumber
C ++ 20 menyediakan cara untuk dengan mudah mengimplementasikan operator perbandingan default.
Contoh dari cppreference.com :
sumber
Point
sebagai contoh untuk operasi pemesanan , karena tidak ada cara standar yang masuk akal untuk memesan dua titik denganx
dany
koordinat ...std::set
untuk memastikan semua poin unik, dan hanyastd::set
menggunakanoperator<
.auto
: Untuk kasus ini, bisakah kita selalu menganggapnyastd::strong_ordering
dari#include <compare>
?std::common_comparison_category_t
, yang untuk kelas ini menjadi pemesanan default (std::strong_ordering
).Tidak mungkin untuk mendefinisikan default
==
, tetapi Anda dapat menentukan default!=
melalui==
mana Anda biasanya harus mendefinisikan sendiri. Untuk ini, Anda harus melakukan hal-hal berikut:Anda dapat melihat http://www.cplusplus.com/reference/std/utility/rel_ops/ untuk detailnya.
Selain itu jika Anda mendefinisikan
operator<
, operator untuk <=,>,> = dapat disimpulkan dari itu ketika menggunakanstd::rel_ops
.Tetapi Anda harus berhati-hati ketika menggunakan
std::rel_ops
karena operator pembanding dapat disimpulkan untuk jenis yang tidak Anda harapkan.Cara yang lebih disukai untuk menyimpulkan operator terkait dari yang dasar adalah menggunakan boost :: operator .
Pendekatan yang digunakan dalam boost lebih baik karena mendefinisikan penggunaan operator untuk kelas yang Anda inginkan, bukan untuk semua kelas dalam ruang lingkup.
Anda juga dapat menghasilkan "+" dari "+ =", - dari "- =", dll ... (lihat daftar lengkap di sini )
sumber
!=
setelah==
operator menulis . Atau saya lakukan tetapi kurangconst
. Harus menulis sendiri juga dan semuanya baik-baik saja.rel_ops
tidak digunakan lagi dalam C ++ 20: karena tidak berfungsi , setidaknya tidak di mana-mana, dan tentu saja tidak secara konsisten. Tidak ada cara yang dapat diandalkansort_decreasing()
untuk mengkompilasi. Di sisi lain, Boost.Operators bekerja dan selalu berfungsi.C ++ 0x
telahmemiliki proposal untuk fungsi-fungsi default, sehingga Anda dapat mengatakandefault operator==;
Kami telah belajar bahwa ini membantu untuk membuat hal-hal ini eksplisit.sumber
operator==
. Sangat disayangkan.Secara konseptual tidak mudah untuk mendefinisikan kesetaraan. Bahkan untuk data POD, orang dapat berargumen bahwa bahkan jika bidangnya sama, tetapi itu adalah objek yang berbeda (pada alamat yang berbeda) tidak harus sama. Ini sebenarnya tergantung pada penggunaan operator. Sayangnya kompiler Anda tidak bersifat psikis dan tidak dapat menyimpulkannya.
Selain itu, fungsi default adalah cara terbaik untuk menembak diri sendiri di kaki. Default yang Anda jelaskan pada dasarnya ada untuk menjaga kompatibilitas dengan POD struct. Namun mereka menyebabkan lebih dari cukup kekacauan dengan pengembang lupa tentang mereka, atau semantik dari implementasi standar.
sumber
int
dibuat melalui copy ctor dari yang lain sama dengan yang dibuatnya; satu-satunya hal logis yang harus dilakukan untukstruct
duaint
bidang adalah bekerja dengan cara yang sama persis.+
operator dalam hal itu non-asosiatif untuk float; itu(x + y) + z
! =x + (y + z)
, karena cara pembulatan FP terjadi. (Bisa dibilang, ini adalah masalah yang jauh lebih buruk daripada==
karena itu benar untuk nilai numerik normal.) Anda mungkin menyarankan menambahkan operator tambahan baru yang bekerja untuk semua jenis numerik (bahkan int) dan hampir persis sama seperti+
tetapi asosiatif ( entah bagaimana). Tetapi kemudian Anda akan menambahkan mengasapi dan kebingungan untuk bahasa tanpa benar-benar membantu banyak orang.Ini mungkin bukan masalah secara fungsional, tetapi dalam hal kinerja, perbandingan anggota-per-anggota standar cenderung lebih sub-optimal daripada penugasan / penyalinan anggota-oleh-anggota standar. Tidak seperti urutan penugasan, urutan perbandingan berdampak pada kinerja karena anggota yang tidak setara pertama menyiratkan yang lainnya dapat dilewati. Jadi, jika ada beberapa anggota yang biasanya sama, Anda ingin membandingkannya terakhir, dan kompilator tidak tahu anggota mana yang lebih mungkin sama.
Pertimbangkan contoh ini, di mana
verboseDescription
string panjang dipilih dari serangkaian deskripsi cuaca yang relatif kecil.(Tentu saja kompiler akan berhak mengabaikan urutan perbandingan jika mengakui bahwa mereka tidak memiliki efek samping, tetapi mungkin masih akan mengambil que dari kode sumber di mana ia tidak memiliki informasi yang lebih baik sendiri.)
sumber
Hanya agar jawaban atas pertanyaan ini tetap lengkap seiring berjalannya waktu: karena C ++ 20 dapat secara otomatis dihasilkan dengan perintah
auto operator<=>(const foo&) const = default;
Ini akan menghasilkan semua operator: ==,! =, <, <=,>, Dan> =, lihat https://en.cppreference.com/w/cpp/language/default_comparisons untuk detailnya.
Karena tampilan operator
<=>
, itu disebut operator pesawat ruang angkasa. Lihat juga Mengapa kita membutuhkan operator pesawat ruang angkasa <=> di C ++? .EDIT: juga di C ++ 11 pengganti yang cukup rapi untuk yang tersedia dengan
std::tie
melihat https://en.cppreference.com/w/cpp/utility/tuple/tie untuk contoh kode lengkap denganbool operator<(…)
. Bagian yang menarik, diubah untuk bekerja dengan==
adalah:std::tie
bekerja dengan semua operator perbandingan, dan sepenuhnya dioptimalkan oleh kompiler.sumber
Saya setuju, untuk kelas tipe POD maka kompiler dapat melakukannya untuk Anda. Namun apa yang Anda anggap sederhana kompiler mungkin salah. Jadi lebih baik membiarkan programmer melakukannya.
Saya pernah memiliki kasus POD di mana dua bidang unik - sehingga perbandingan tidak akan pernah dianggap benar. Namun perbandingan yang saya butuhkan hanya pernah dibandingkan pada payload - sesuatu yang kompiler tidak akan pernah mengerti atau pernah bisa mengetahuinya sendiri.
Selain itu - mereka tidak butuh waktu lama untuk menulis, kan ?!
sumber