std::swap()
digunakan oleh banyak kontainer std (seperti std::list
dan std::vector
) selama penyortiran dan bahkan penugasan.
Tetapi implementasi std swap()
sangat umum dan agak tidak efisien untuk tipe kustom.
Dengan demikian, efisiensi dapat diperoleh dengan membebani std::swap()
dengan implementasi khusus tipe kustom. Tetapi bagaimana Anda bisa menerapkannya sehingga akan digunakan oleh kontainer std?
Jawaban:
Cara yang benar untuk membebani swap adalah dengan menuliskannya di namespace yang sama dengan apa yang Anda tukar, sehingga dapat ditemukan melalui pencarian yang bergantung pada argumen (ADL) . Satu hal yang sangat mudah dilakukan adalah:
sumber
std::sort
yang menggunakan ADL untuk menukar elemen tidak sesuai dengan C ++ 03 tetapi sesuai dengan C ++ 11. Juga, mengapa -1 jawaban berdasarkan fakta bahwa klien mungkin menggunakan kode non-idiomatik?Perhatian Mozza314
Berikut adalah simulasi dari efek
std::algorithm
pemanggilan generikstd::swap
, dan meminta pengguna menyediakan swap mereka di namespace std. Karena ini adalah sebuah eksperimen, simulasi ini menggunakannamespace exp
bukannamespace std
.Bagi saya ini cetakannya:
Jika kompilator Anda mencetak sesuatu yang berbeda maka kompilator tidak menerapkan "pencarian dua fase" untuk template dengan benar.
Jika kompiler Anda sesuai (ke salah satu dari C ++ 98/03/11), maka itu akan memberikan keluaran yang sama yang saya tunjukkan. Dan dalam kasus seperti itu, apa yang Anda takuti akan terjadi, benar-benar terjadi. Dan memasukkan Anda
swap
ke dalam namespacestd
(exp
) tidak menghentikannya terjadi.Dave dan saya adalah anggota komite dan telah mengerjakan bidang standar ini selama satu dekade (dan tidak selalu sesuai satu sama lain). Tetapi masalah ini telah diselesaikan untuk waktu yang lama, dan kami berdua sepakat tentang cara menyelesaikannya. Abaikan pendapat / jawaban ahli Dave di bidang ini atas risiko Anda sendiri.
Masalah ini terungkap setelah C ++ 98 diterbitkan. Mulai sekitar 2001 Dave dan saya mulai mengerjakan bidang ini . Dan inilah solusi modern:
Outputnya adalah:
Memperbarui
Pengamatan telah dilakukan bahwa:
berhasil! Jadi mengapa tidak menggunakan itu?
Pertimbangkan kasus Anda
A
adalah template kelas:Sekarang tidak berfungsi lagi. :-(
Jadi Anda bisa memasukkan
swap
namespace std dan membuatnya berfungsi. Tapi Anda harus ingat untuk menempatkanswap
diA
's namespace untuk kasus ketika Anda memiliki template:A<T>
. Dan karena kedua kasus akan bekerja jika Anda meletakkanswap
diA
's namespace, itu hanya lebih mudah untuk mengingat (dan mengajar orang lain) untuk hanya melakukannya bahwa salah satu cara.sumber
template <>
contoh pertama Anda, saya mendapatkan outputexp::swap(A, A)
dari gcc. Jadi, mengapa tidak lebih memilih spesialisasi?using std::swap
merupakan pengecualian untuk aturan "jangan pernah meletakkan pernyataan menggunakan di dalam file header"? Sebenarnya, kenapa tidak dimasukkan keusing std::swap
dalam<algorithm>
? Saya kira itu bisa mematahkan sebagian kecil kode orang. Mungkin menolak dukungan dan akhirnya memasukkannya?using std::swap
ruang lingkup fungsi dalam header Anda. Ya,swap
hampir menjadi kata kunci. Tapi tidak, ini bukan kata kunci yang tepat. Jadi sebaiknya jangan mengekspornya ke semua namespace sampai Anda benar-benar harus melakukannya.swap
sangat miripoperator==
. Perbedaan terbesar adalah bahwa tidak ada yang pernah berpikir untuk meneleponoperator==
dengan sintaks namespace yang memenuhi syarat (itu akan terlalu jelek).Anda tidak diizinkan (oleh standar C ++) untuk membebani std :: swap, namun Anda secara khusus diizinkan untuk menambahkan spesialisasi template untuk tipe Anda sendiri ke namespace std. Misalnya
maka penggunaan di std containers (dan di mana pun) akan memilih spesialisasi Anda, bukan yang umum.
Perhatikan juga bahwa menyediakan implementasi kelas dasar swap tidak cukup baik untuk tipe turunan Anda. Misalnya jika sudah
ini akan berfungsi untuk kelas Basis, tetapi jika Anda mencoba menukar dua objek Turunan, ini akan menggunakan versi generik dari std karena swap templated sama persis (dan ini menghindari masalah hanya menukar bagian 'dasar' dari objek turunan Anda ).
CATATAN: Saya telah memperbarui ini untuk menghapus bit yang salah dari jawaban terakhir saya. D'oh! (terima kasih puetzk dan j_random_hacker untuk menunjukkannya)
sumber
Meskipun benar bahwa seseorang seharusnya tidak menambahkan barang ke std :: namespace, menambahkan spesialisasi template untuk tipe yang ditentukan pengguna secara khusus diperbolehkan. Membebani fungsi tidak. Ini adalah perbedaan halus :-)
Spesialisasi dari std :: swap akan terlihat seperti ini:
Tanpa template <> bit itu akan menjadi overload, yang tidak terdefinisi, bukan spesialisasi, yang diijinkan. @ Wilka menyarankan pendekatan untuk mengubah namespace default dapat bekerja dengan kode pengguna (karena pencarian Koenig lebih memilih versi tanpa namespace) tetapi itu tidak dijamin, dan pada kenyataannya tidak benar-benar seharusnya (implementasi STL harus menggunakan sepenuhnya -qualified std :: swap).
Ada utas di comp.lang.c ++. Yang dimoderasi dengan diskusi panjang tentang topik. Sebagian besar tentang spesialisasi parsial, meskipun (yang saat ini tidak ada cara yang baik untuk melakukannya).
sumber
vector
versi dan akan digunakan .::swap
kelebihan Anda menambahkan lebih khusus daripadastd::swap
yang berlebihan untukvector
, sehingga menangkap panggilan dan tidak ada spesialisasi yang terakhir relevan. Saya tidak yakin bagaimana itu masalah praktis (tetapi saya juga tidak mengklaim bahwa ini adalah ide yang bagus!).