Ada beberapa cara untuk menulis swap
, beberapa lebih baik daripada yang lain. Namun seiring waktu, ditemukan satu definisi yang paling baik. Mari kita pikirkan bagaimana kita berpikir tentang menulis suatu swap
fungsi.
Kami pertama kali melihat bahwa wadah seperti std::vector<>
memiliki fungsi anggota argumen tunggal swap
, seperti:
struct vector
{
void swap(vector&) { /* swap members */ }
};
Jadi, tentu saja, kelas kita juga seharusnya, kan? Yah, tidak juga. Perpustakaan standar memiliki segala macam hal yang tidak perlu , dan anggota swap
adalah salah satunya. Mengapa? Ayo pergi.
Yang harus kita lakukan adalah mengidentifikasi apa yang kanonik, dan apa yang perlu dilakukan kelas kita untuk mengatasinya. Dan metode kanonik swapping adalah dengan std::swap
. Inilah sebabnya mengapa fungsi anggota tidak berguna: mereka bukan bagaimana kita harus bertukar sesuatu, secara umum, dan tidak berpengaruh pada perilaku std::swap
.
Kalau begitu, untuk membuat std::swap
pekerjaan kita harus menyediakan (dan std::vector<>
seharusnya menyediakan) spesialisasi std::swap
, kan?
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
Yah itu pasti akan berhasil dalam kasus ini, tetapi ia memiliki masalah mencolok: fungsi spesialisasi tidak dapat parsial. Artinya, kami tidak dapat mengkhususkan kelas template dengan ini, hanya instantiations tertentu:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
Metode ini berfungsi beberapa saat, tetapi tidak selalu. Pasti ada cara yang lebih baik.
Ada! Kita dapat menggunakan suatu friend
fungsi, dan menemukannya melalui ADL :
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
Ketika kami ingin menukar sesuatu, kami kaitkan † std::swap
dan kemudian membuat panggilan yang tidak memenuhi syarat:
using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first
// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap
Apa itu friend
fungsi? Ada kebingungan di sekitar area ini.
Sebelum C ++ distandarisasi, friend
fungsi melakukan sesuatu yang disebut "injeksi nama teman", di mana kode berperilaku seolah-olah fungsi tersebut telah ditulis dalam namespace sekitarnya. Misalnya, ini adalah pra-standar yang setara:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
Namun, ketika ADL ditemukan ini dihapus. The friend
Fungsi bisa kemudian hanya ditemukan melalui ADL; jika Anda menginginkannya sebagai fungsi bebas, itu perlu dinyatakan demikian ( lihat ini , misalnya). Tapi lihat! Ada masalah.
Jika Anda hanya menggunakan std::swap(x, y)
, kelebihan Anda tidak akan pernah ditemukan, karena Anda telah secara eksplisit mengatakan "lihat std
, dan tempat lain"! Inilah sebabnya mengapa beberapa orang menyarankan untuk menulis dua fungsi: satu sebagai fungsi yang dapat ditemukan melalui ADL , dan yang lainnya untuk menangani std::
kualifikasi eksplisit .
Tapi seperti yang kita lihat, ini tidak bisa bekerja di semua kasus, dan kita berakhir dengan kekacauan yang jelek. Alih-alih, bertukar idiomatik memilih jalan lain: alih-alih menjadikannya tugas kelas untuk menyediakan std::swap
, itu adalah tugas para penukar untuk memastikan mereka tidak menggunakan kualifikasi swap
, seperti di atas. Dan ini cenderung bekerja dengan baik, selama orang tahu tentang itu. Tapi di situlah masalahnya: tidak perlu untuk menggunakan panggilan yang tidak memenuhi syarat!
Untuk membuat ini lebih mudah, beberapa perpustakaan seperti Boost menyediakan fungsi boost::swap
, yang hanya melakukan panggilan wajar tanpa pengecualian swap
, dengan std::swap
sebagai namespace terkait. Ini membantu membuat hal-hal ringkas lagi, tetapi masih mengecewakan.
Perhatikan bahwa tidak ada perubahan dalam C ++ 11 untuk perilaku std::swap
, yang saya dan orang lain salah sangka akan menjadi kasusnya. Jika Anda kesal dengan ini, baca di sini .
Singkatnya: fungsi anggota hanyalah noise, spesialisasi jelek dan tidak lengkap, tetapi friend
fungsinya lengkap dan berfungsi. Dan saat Anda bertukar, gunakan boost::swap
atau tidak berkualifikasi swap
dengan std::swap
terkait.
† Secara informal, nama dikaitkan jika akan dipertimbangkan selama panggilan fungsi. Untuk detailnya, baca §3.4.2. Dalam hal ini, std::swap
biasanya tidak dipertimbangkan; tetapi kami dapat mengaitkannya (menambahkannya ke kumpulan kelebihan yang dianggap tidak memenuhi syarat swap
), memungkinkannya ditemukan.