Saya seorang programmer C ++ dengan pengalaman terbatas.
Andaikata saya ingin menggunakan suatu STL map
untuk menyimpan dan memanipulasi beberapa data, saya ingin tahu apakah ada perbedaan yang berarti (juga dalam kinerja) antara kedua pendekatan struktur data tersebut:
Choice 1:
map<int, pair<string, bool> >
Choice 2:
struct Ente {
string name;
bool flag;
}
map<int, Ente>
Secara khusus, apakah ada overhead menggunakan struct
bukan sederhana pair
?
c++
data-structures
stl
Marco Stramezzi
sumber
sumber
std::pair
adalah sebuah struct.std::pair
adalah templat .std::pair<string, bool>
adalah sebuah struct.pair
sepenuhnya tanpa semantik. Tidak ada yang membaca kode Anda (termasuk Anda di masa depan) akan tahu itue.first
adalah nama sesuatu kecuali Anda secara eksplisit menunjukkannya. Saya sangat percaya bahwapair
itu adalah tambahan yang sangat miskin dan malasstd
, dan bahwa ketika dikandung tidak ada yang berpikir "tetapi suatu hari, semua orang akan menggunakan ini untuk segala sesuatu yang dua hal, dan tidak ada yang akan tahu apa arti kode siapa pun ".map
iterator bukan pengecualian yang valid. ("pertama" = kunci dan "kedua" = nilai ... sungguhstd
,? Benarkah?)Jawaban:
Pilihan 1 ok untuk hal-hal "digunakan hanya sekali" kecil. Pada dasarnya
std::pair
masih sebuah struct. Seperti yang dinyatakan oleh komentar ini, pilihan 1 akan mengarah ke kode yang sangat jelek di suatu tempat di bawah lubang kelinci sepertithing.second->first.second->second
dan tidak ada yang benar-benar ingin menguraikan itu.Pilihan 2 lebih baik untuk yang lainnya, karena lebih mudah untuk membaca apa arti dari hal-hal di peta. Ini juga lebih fleksibel jika Anda ingin mengubah data (misalnya ketika Ente tiba-tiba membutuhkan bendera lain). Kinerja seharusnya tidak menjadi masalah di sini.
sumber
Kinerja :
Tergantung.
Dalam kasus khusus Anda, tidak akan ada perbedaan kinerja karena keduanya akan diletakkan dalam memori yang sama.
Dalam kasus yang sangat spesifik (jika Anda menggunakan struct kosong sebagai salah satu anggota data) maka
std::pair<>
berpotensi menggunakan Empty Base Optimization (EBO) dan memiliki ukuran yang lebih rendah daripada setara struct. Dan ukuran yang lebih rendah umumnya berarti kinerja yang lebih tinggi:Cetakan: 32, 32, 40, 40 di ideone .
Catatan: Saya tidak mengetahui adanya implementasi yang benar-benar menggunakan trik EBO untuk pasangan reguler, namun umumnya digunakan untuk tupel.
Keterbacaan :
Terlepas dari optimasi mikro, struktur bernama lebih ergonomis.
Maksudku,
map[k].first
tidak seburuk itu sementaraget<0>(map[k])
hampir tidak bisa dimengerti. Kontras denganmap[k].name
yang segera menunjukkan apa yang kita baca.Ini semua menjadi lebih penting ketika tipe-tipe tersebut dapat dipertukarkan satu sama lain, karena bertukar secara tidak sengaja menjadi perhatian nyata.
Anda mungkin juga ingin membaca tentang Pengetikan Struktural vs Nominal.
Ente
adalah jenis khusus yang hanya dapat dioperasikan oleh hal-hal yang diharapkanEnte
, apa pun yang dapat beroperasistd::pair<std::string, bool>
dapat beroperasi pada mereka ... bahkan ketikastd::string
ataubool
tidak berisi apa yang mereka harapkan, karenastd::pair
tidak memiliki semantik yang terkait dengannya.Perawatan :
Dalam hal perawatan,
pair
adalah yang terburuk. Anda tidak dapat menambahkan bidang.tuple
lebih baik dalam hal itu, selama Anda menambahkan bidang baru semua bidang yang ada masih diakses oleh indeks yang sama. Yang tidak dapat dipahami seperti sebelumnya tetapi setidaknya Anda tidak perlu pergi dan memperbaruinya.struct
adalah pemenang yang jelas. Anda dapat menambahkan bidang di mana pun Anda suka.Kesimpulannya:
pair
adalah yang terburuk dari kedua dunia,tuple
mungkin memiliki sedikit keunggulan dalam kasus yang sangat spesifik (tipe kosong),struct
.Catatan: jika Anda menggunakan getter, maka Anda dapat menggunakan trik basis kosong sendiri tanpa klien harus mengetahuinya seperti pada
struct Thing: Empty { std::string name; }
; itulah sebabnya enkapsulasi adalah topik berikutnya yang harus Anda perhatikan sendiri.sumber
first
dansecond
, tidak ada tempat untuk Empty Base Optimization untuk memulai.pair
yang akan menggunakan EBO, tetapi kompiler dapat menyediakan built-in. Namun, karena mereka dianggap anggota, maka dapat diamati (memeriksa alamat mereka, misalnya) dalam hal ini tidak akan sesuai.[[no_unique_address]]
, yang memungkinkan setara dengan EBO untuk anggota.Pair paling bersinar ketika digunakan sebagai tipe pengembalian fungsi bersama dengan penugasan yang dirusak menggunakan std :: tie dan C ++ 17's binding binding. Menggunakan std :: tie:
Menggunakan ikatan terstruktur C ++ 17:
Contoh buruk penggunaan std :: pair (atau tuple) akan menjadi seperti ini:
karena tidak jelas ketika memanggil std :: get <2> (player_data) apa yang disimpan di indeks posisi 2. Ingat keterbacaan dan membuatnya jelas bagi pembaca apa yang dilakukan kode itu penting . Pertimbangkan bahwa ini jauh lebih mudah dibaca:
Secara umum Anda harus berpikir tentang std :: pair dan std :: tuple sebagai cara untuk mengembalikan lebih dari 1 objek dari suatu fungsi. Aturan praktis yang saya gunakan (dan telah melihat banyak orang lain juga menggunakan) adalah bahwa objek dikembalikan dalam std :: tuple atau std :: pair hanya "terkait" dalam konteks membuat panggilan ke fungsi yang mengembalikannya atau dalam konteks struktur data yang menautkan keduanya (mis. std :: map menggunakan std :: pair untuk tipe penyimpanannya). Jika hubungan ada di tempat lain dalam kode Anda, Anda harus menggunakan struct.
Bagian terkait dari Pedoman Inti:
sumber