bagaimana cara menyediakan fungsi swap untuk kelas saya?

89

Apa cara yang tepat untuk mengaktifkan swapalgoritma STL saya ?

1) Anggota swap. Apakah std::swapmenggunakan trik SFINAE untuk menggunakan anggota swap.

2) Berdiri bebas swapdi namespace yang sama.

3) Spesialisasi parsial std::swap.

4) Semua hal di atas.

Terima kasih.

EDIT: Sepertinya saya tidak mengucapkan pertanyaan saya dengan jelas. Pada dasarnya, saya memiliki kelas template dan saya membutuhkan STL algos untuk menggunakan metode swap (efisien) yang saya tulis untuk kelas itu.

gambar11
sumber

Jawaban:

95
  1. adalah tepat penggunaan dari swap. Tulis dengan cara ini ketika Anda menulis kode "library" dan ingin mengaktifkan ADL (pencarian yang bergantung pada argumen) swap. Juga, ini tidak ada hubungannya dengan SFINAE.
// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs) {
    using std::swap; // enable 'std::swap' to be found
                    // if no other 'swap' is found through ADL
    // some code ...
    swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                    // or falls back on 'std::swap'
    // more code ...
}
  1. Adalah cara yang tepat untuk menyediakan swapfungsi untuk kelas Anda.
namespace Foo {

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs) {
    // ...
}

}

Jika swapsekarang digunakan seperti yang ditunjukkan pada 1), fungsi Anda akan ditemukan. Juga, Anda dapat menjadikan fungsi itu sebagai teman jika Anda benar-benar membutuhkannya, atau menyediakan anggota swapyang dipanggil dengan fungsi gratis:

// version 1
class Bar{
public:
    friend void swap(Bar& lhs, Bar& rhs) {
    // ....
    }
};

// version 2
class Bar{
public:
    void swap(Bar& other) {
    // ...
    }
};

void swap(Bar& lhs, Bar& rhs) {
    lhs.swap(rhs);
}

...
  1. Yang Anda maksud adalah spesialisasi eksplisit. Partial masih merupakan sesuatu yang lain dan juga tidak mungkin untuk fungsi, hanya struct / class. Dengan demikian, karena Anda tidak dapat mengkhususkan diri std::swapuntuk kelas template, Anda harus menyediakan fungsi gratis di namespace Anda. Bukan hal yang buruk, jika boleh saya katakan. Sekarang, spesialisasi eksplisit juga dimungkinkan, tetapi umumnya Anda tidak ingin mengkhususkan template fungsi :
namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
    // ...
}

}
  1. Tidak, karena 1) berbeda dari 2) dan 3). Juga, memiliki 2) dan 3) akan menyebabkan selalu 2) memilih, karena lebih cocok.
Xeo
sumber
8
Anda (1) dan pertanyaan (1) tidak benar-benar sejalan, kecuali saya salah membaca sesuatu. Tetap saja, +1
Dennis Zickefoose
1
@Eo. Terima kasih atas masukan Anda. Saya mengedit pertanyaan saya. Apakah STL menggunakan swap seperti yang Anda jelaskan pada kasus 1?
pic11
1
@pic: Ya, STL akan menggunakan swap ADL yang saya tunjukkan di 1), tetapi hanya jika itu ada sebagai fungsi gratis, tidak hanya fungsi anggota. Lihat 2) dan 3), kedua versi akan dipilih oleh algoritme. Saya akan menyarankan 2), karena 3) sudah ketinggalan zaman dan dianggap praktik yang buruk.
Xeo
2
Komentar di bagian pertama kode menyesatkan. using std::swap;tidak mengaktifkan ADL, ini hanya memungkinkan kompiler untuk mencari lokasi std::swapjika ADL tidak menemukan kelebihan beban yang sesuai.
David Rodríguez - dribeas
6
Jawaban ini secara teknis benar, tetapi sangat perlu diedit agar lebih jelas. OP (1) bukanlah jawaban yang benar karena pembacaan yang terlalu cepat dalam jawaban ini tampaknya salah mengindikasikan.
Howard Hinnant
1

Untuk menjawab EDIT, di mana kelas mungkin kelas template, Anda tidak perlu spesialisasi sama sekali. pertimbangkan kelas seperti ini:

template <class T>
struct vec3
{
    T x,y,z;
};

Anda dapat mendefinisikan kelas-kelas seperti:

vec3<float> a;
vec3<double> b;
vec3<int> c;

jika Anda ingin dapat membuat satu fungsi untuk mengimplementasikan semua 3 swap (bukan kelas contoh ini menjaminnya) Anda melakukan seperti yang dikatakan Xeo di (2) ... tanpa spesialisasi tetapi cukup buat fungsi template biasa:

template <class T>
void swap(vec3<T> &a, vec3<T> &b)
{
    using std::swap;
    swap(a.x,b.x);
    swap(a.y,b.y);
    swap(a.z,b.z);
}

Fungsi template swap harus ditempatkan di namespace yang sama dengan kelas yang Anda coba tukar. metode berikut akan menemukan dan menggunakan swap itu meskipun Anda tidak mereferensikan namespace itu menggunakan ADL:

using std::swap;
swap(a,b);
Ben
sumber
0

Tampaknya (2) ( berdiri bebas swapdi namespace yang sama di mana kelas yang ditentukan pengguna dideklarasikan ) adalah satu-satunya cara yang diizinkan untuk menyediakan swapkelas yang ditentukan pengguna, karena menambahkan deklarasi ke namespace stdumumnya merupakan perilaku yang tidak ditentukan. Memperluas namespace std (cppreference.com) :

Ini adalah perilaku yang tidak ditentukan untuk menambahkan deklarasi atau definisi ke namespace stdatau ke namespace yang bersarang di dalamnya std, dengan beberapa pengecualian yang disebutkan di bawah ini

Dan swaptidak dilambangkan sebagai salah satu pengecualian itu. Jadi menambahkan swapkelebihan Anda sendiri ke stdnamespace adalah perilaku yang tidak ditentukan.

Juga dikatakan bahwa pustaka standar menggunakan panggilan yang tidak memenuhi syarat ke swapfungsi untuk memanggil yang ditentukan swappengguna untuk kelas pengguna jika ditentukan pengguna swaptersebut disediakan.

Dapat ditukar (cppreference.com) :

Banyak fungsi pustaka standar (misalnya, banyak algoritme) mengharapkan argumennya memenuhi Swappable , yang berarti bahwa setiap kali pustaka standar melakukan swap, ia menggunakan padanannya using std::swap; swap(t, u);.

swap (www.cplusplus.com) :

Banyak komponen pustaka standar (di dalam std) memanggil swapdengan cara yang tidak memenuhi syarat untuk memungkinkan pemanggilan overload kustom untuk tipe non-fundamental daripada versi generik ini: Overload kustom swapdideklarasikan di namespace yang sama dengan jenis yang mereka sediakan dipilih melalui pencarian yang bergantung pada argumen atas versi umum ini.

Namun perhatikan bahwa secara langsung menggunakan std::swapfungsi untuk kelas yang ditentukan pengguna akan memanggil versi generik, std::swapbukan yang ditentukan pengguna swap:

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

Jadi direkomendasikan untuk memanggil swapfungsi dalam kode pengguna dengan cara yang sama seperti yang dilakukan di pustaka standar:

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
anton_rh
sumber