std::swap()
ini digunakan oleh banyak std wadah (seperti std::daftar
dan std::vektor
) selama penyortiran dan bahkan tugas.
Tapi std implementasi dari swap()
adalah sangat umum dan lebih efisien untuk jenis kustom.
Dengan demikian efisiensi dapat diperoleh dengan overloading std::swap()
dengan jenis kustom spesifik implementasi. Tapi bagaimana anda bisa menerapkan hal ini sehingga akan digunakan oleh std wadah?
Cara yang tepat untuk overload swap adalah untuk menulis di namespace yang sama seperti apa yang anda're swapping, sehingga dapat ditemukan melalui argumen tergantung lookup (ADL). Salah satu yang sangat mudah untuk dilakukan adalah:
class X
{
// ...
friend void swap(X& a, X& b)
{
using std::swap; // bring in swap for built-in types
swap(a.base1, b.base1);
swap(a.base2, b.base2);
// ...
swap(a.member1, b.member1);
swap(a.member2, b.member2);
// ...
}
};
Perhatian Mozza314
Berikut adalah simulasi dari efek generik std::algoritma
memanggil std::swap
, dan memiliki pengguna memberikan mereka swap dalam namespace std. Karena ini adalah sebuah eksperimen, simulasi ini menggunakan namespace exp
bukan namespace std
.
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
exp::swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
namespace exp
{
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Bagi saya ini akan mencetak:
generic exp::swap
Jika anda compiler mencetak sesuatu yang berbeda maka itu adalah tidak benar menerapkan "dua-fase lookup" untuk template.
Jika anda compiler yang sesuai (untuk setiap C++98/03/11), maka akan memberikan output yang sama saya lihat. Dan dalam kasus yang persis apa yang anda takut akan terjadi, tidak terjadi. Dan menempatkan anda swap
ke namespace std
(exp
) tidak menghentikan hal itu terjadi.
Dave dan saya keduanya adalah anggota komite dan telah bekerja di daerah ini dari standar selama satu dekade (dan tidak selalu dalam perjanjian dengan satu sama lain). Tapi masalah ini telah menetap untuk waktu yang lama, dan kami berdua setuju pada bagaimana hal itu telah diselesaikan. Mengabaikan Dave's ahli pendapat/menjawab di daerah ini di risiko anda sendiri.
Masalah ini terungkap setelah C++98 diterbitkan. Mulai tahun 2001 tentang Dave dan saya mulai pekerjaan daerah ini. Dan ini adalah solusi modern:
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
void swap(A&, A&)
{
printf("swap(A, A)\n");
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Output adalah:
swap(A, A)
Update
Sebuah pengamatan yang telah dilakukan bahwa:
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
karya-karya! Jadi mengapa tidak menggunakannya?
Pertimbangkan kasus yang A
adalah kelas template:
// simulate user code which includes <algorithm>
template <class T>
struct A
{
};
namespace exp
{
template <class T>
void swap(A<T>&, A<T>&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A<int> a[2];
exp::algorithm(a, a+2);
}
Sekarang itu doesn't bekerja lagi. :-(
Jadi anda bisa menempatkan swap
di namespace std dan telah bekerja. Tapi anda'll harus ingat untuk menempatkan swap
di A
's namespace untuk kasus ketika anda memiliki template: A<T>
. Dan karena kedua kasus akan bekerja jika anda menempatkan swap
di A
's namespace, itu hanya lebih mudah untuk diingat (dan untuk mengajar orang lain) untuk hanya melakukan itu salah satu cara.
Anda're tidak diperbolehkan (oleh standar C++) untuk overload std::swap, namun anda secara khusus diijinkan untuk menambahkan template spesialisasi untuk anda sendiri jenis std namespace. E. g.
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}
kemudian penggunaan di std wadah (dan tempat lain) akan memilih spesialisasi anda bukan satu umum.
Juga catatan yang menyediakan kelas dasar pelaksanaan swap isn't cukup baik bagi anda yang berasal jenis. E. g. jika anda memiliki
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}
ini akan bekerja untuk kelas Dasar, tetapi jika anda mencoba untuk swap dua Berasal dari benda-benda itu akan menggunakan versi generik dari std karena kerangka swap sama persis (dan menghindari masalah hanya menukar 'dasar' bagian-bagian dari anda yang berasal objek).
CATATAN: saya've diperbarui ini untuk menghapus bit yang salah dari jawaban terakhir saya. D'oh! (terima kasih puetzk dan j_random_hacker untuk menunjuk keluar)
Sementara itu's benar bahwa seseorang tidak't umumnya menambahkan barang ke std:: namespace, menambahkan template spesialisasi untuk user-defined jenis ini secara khusus diizinkan. Overloading fungsi tidak. Ini adalah perbedaan yang halus :-)
17.4.3.1/1 Hal ini didefinisikan untuk program C++ untuk menambahkan deklarasi atau definisi untuk namespace std atau namespaces dengan namespace std kecuali ditentukan. Sebuah program dapat menambahkan template untuk setiap spesialisasi standar perpustakaan template untuk namespace std. Seperti spesialisasi (lengkap atau parsial) dari perpustakaan standar hasil di terdefinisi perilaku kecuali deklarasi tergantung pada user-defined nama hubungan eksternal dan kecuali template spesialisasi memenuhi standar perpustakaan persyaratan untuk template asli.
Spesialisasi dari std::swap
namespace std
{
template<>
void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}
Tanpa template<> bit ini akan menjadi suatu yang berlebihan, yang tidak terdefinisi, daripada spesialisasi, yang diizinkan. @Wilka's menyarankan pendekatan untuk mengubah default namespace dapat bekerja dengan kode pengguna (karena Koenig lookup lebih memilih namespace-kurang versi) tapi itu's tidak dijamin, dan bahkan isn't benar-benar seharusnya (STL pelaksanaan harus menggunakan sepenuhnya memenuhi syarat std::swap).
Ada benang di comp.lang.c++.moderated dengan panjang **** diskusi dari topik. Kebanyakan adalah parsial spesialisasi, meskipun (yang ada's saat ini tidak ada cara yang baik untuk melakukan).