Apakah aman untuk menukar dua vektor berbeda dalam C ++, menggunakan metode std :: vector :: swap?

30

Misalkan Anda memiliki kode berikut:

#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> First{"example", "second" , "C++" , "Hello world" };
    std::vector<std::string> Second{"Hello"};

    First.swap(Second);

    for(auto a : Second) std::cout << a << "\n";
    return 0;
}

Bayangkan vektor tidak std::string, belum kelas:

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

Apakah masih aman untuk menukar kedua vektor dengan std::vector::swapmetode: WidgetVector.swap(Widget2Vector);atau itu akan mengarah ke UB?

Emanuele Oggiano
sumber

Jawaban:

20

Aman karena tidak ada yang dibuat selama operasi swap. Hanya anggota data kelas std::vectoryang ditukar.

Pertimbangkan program demonstratif berikut yang memperjelas bagaimana objek kelas std::vectordipertukarkan.

#include <iostream>
#include <utility>
#include <iterator>
#include <algorithm>
#include <numeric>

class A
{
public:
    explicit A( size_t n ) : ptr( new int[n]() ), n( n )
    {
        std::iota( ptr, ptr + n, 0 );   
    }

    ~A() 
    { 
        delete []ptr; 
    }

    void swap( A & a ) noexcept
    {
        std::swap( ptr, a.ptr );
        std::swap( n, a.n );
    }

    friend std::ostream & operator <<( std::ostream &os, const A &a )
    {
        std::copy( a.ptr, a.ptr + a.n, std::ostream_iterator<int>( os, " " ) );
        return os;
    }

private:    
    int *ptr;
    size_t n;
};

int main() 
{
    A a1( 10 );
    A a2( 5 );

    std::cout << a1 << '\n';
    std::cout << a2 << '\n';

    std::cout << '\n';

    a1.swap( a2 );

    std::cout << a1 << '\n';
    std::cout << a2 << '\n';

    std::cout << '\n';

    return 0;
}

Output program adalah

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 

0 1 2 3 4 
0 1 2 3 4 5 6 7 8 9 

Seperti yang Anda lihat hanya anggota data ptrdan nditukar dalam fungsi swap anggota. Tidak ada sumber daya tambahan yang digunakan.

Pendekatan serupa digunakan di kelas std::vector.

Adapun contoh ini

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

lalu ada objek dari kelas yang berbeda. Fungsi swap anggota diterapkan pada vektor dengan tipe yang sama.

Vlad dari Moskow
sumber
6
Tetapi bagaimana dengan kasus OP yang sebenarnya , di mana vektor yang ditukar dari kelas yang berbeda ?
Adrian Mole
4
@AdrianMole Fungsi anggota bertukar jika ditentukan untuk jenis vektor yang diberikan. Ini tidak didefinisikan untuk vektor dari jenis yang berbeda. Ini bukan fungsi anggota template.
Vlad dari Moskow
"swap diterapkan ke vektor dari jenis yang sama" Anda harus menambahkan "hanya" antara "adalah" dan "diterapkan".
SS Anne
Tentu saja, pengalokasi negara mungkin dapat mengubah hal-hal.
Deduplicator
21

Ya, ini sangat aman untuk menukar vektor dari jenis yang sama.

Vektor di bawah tenda hanyalah beberapa petunjuk yang menunjuk ke data yang digunakan vektor dan "akhir" urutannya. Saat Anda memanggil swap, Anda cukup menukar pointer tersebut di antara vektor. Anda tidak perlu khawatir bahwa vektor berukuran sama karena ini.

Vektor dari berbagai jenis tidak dapat ditukar menggunakan swap. Anda harus mengimplementasikan fungsi Anda sendiri yang melakukan konversi dan pertukaran.

NathanOliver
sumber
3
Anda perlu melihat lebih dekat pada bagian kedua dari pertanyaan.
Mark Ransom
@MarkRansom Yap. Melewatkan 2. Diperbarui.
NathanOliver
13

Apakah aman untuk menukar dua vektor berbeda dalam C ++, menggunakan metode std :: vector :: swap?

Iya. Bertukar secara umum dapat dianggap aman. Di sisi lain, keselamatan bersifat subyektif dan relatif dan dapat dipertimbangkan dari berbagai perspektif. Karena itu, tidak mungkin untuk memberikan jawaban yang memuaskan tanpa menambah pertanyaan dengan konteks, dan memilih jenis keamanan apa yang dipertimbangkan.

Apakah masih aman untuk menukar kedua vektor dengan metode std :: vector :: swap: WidgetVector.swap (Widget2Vector); atau akan mengarah ke UB?

Tidak akan ada UB. Ya, itu masih aman dalam arti bahwa program ini dibuat dengan buruk.

eerorika
sumber
7

The swapFungsi didefinisikan sebagai berikut: void swap( T& a, T& b );. Perhatikan di sini bahwa keduanya adan b(dan harus ) jenis yang sama . (Tidak ada fungsi yang didefinisikan dengan tanda tangan ini:, void swap( T1& a, T2& b )karena tidak masuk akal!)

Demikian pula, swap()fungsi anggota std::vectorkelas didefinisikan sebagai berikut:

template<class T1> class vector // Note: simplified from the ACTUAL STL definition
{
//...
public:
    void swap( vector& other );
//...
};

Sekarang, karena tidak ada definisi 'setara' dengan override templat (lihat Spesialisasi eksplisit templat fungsi ) untuk parameter fungsi (yang akan berbentuk template <typename T2> void swap(std::vector<T2>& other):), parameter itu harus berupa vektor dengan tipe yang sama (templat) seperti kelas 'pemanggilan' (yaitu, harus juga a vector<T1>).

Anda std::vector<Widget>dan std::vector<Widget2>dua yang berbeda jenis, sehingga panggilan untuk swaptidak akan mengkompilasi, apakah Anda mencoba untuk menggunakan fungsi anggota baik objek (seperti kode Anda tidak), atau menggunakan spesialisasi dari std::swap()fungsi yang mengambil dua std:vectorbenda sebagai parameter.

Adrian Mole
sumber
8
std::vector::swapitu fungsi anggota, bagaimana bisa itu menjadi spesialisasi fungsi templat yang berdiri sendiri ???
Aconcagua
1
Hasil yang benar, alasan yang salah.
NathanOliver
2
@AdrianMole Meskipun memang ada spesialisasi untuk std::swap, bukan itu yang digunakan OP. Ketika Anda melakukannya First.swap(Second);, Anda memanggil std::vector::swapfungsi yang berbeda daristd::swap
NathanOliver
1
Maaf, salah saya ... Tautan Anda langsung menuju ke dokumentasi std :: swap spesialisasi untuk vektor. Tapi itu berbeda dari fungsi anggota , yang ada juga dan digunakan dalam pertanyaan (hanya).
Aconcagua
2
Penalaran sebenarnya analog: Anggota didefinisikan sebagai void swap(std::vector& other)(yaitu std::vector<T>), bukan sebagai template <typename U> void swap(std::vector<U>& other)(dengan asumsi T adalah parameter tipe untuk vektor itu sendiri).
Aconcagua
4

Anda tidak dapat menukar vektor dari dua jenis tetapi ini merupakan kesalahan kompilasi alih-alih UB. vector::swaphanya menerima vektor dengan jenis dan pengalokasi yang sama.

Tidak yakin apakah ini akan berhasil, tetapi jika Anda ingin vektor yang mengandung Widget2s dikonversi dari Widgets Anda dapat mencoba ini:

std::vector<Widget2> Widget2Vector(
    std::make_move_iterator(WidgetVector.begin()),
    std::make_move_iterator(WidgetVector.end())
);

Widget2harus dipindahkan dari yang konstruktif Widget.

pengguna233009
sumber
0

using std::swap; swap(a, b);dan a.swap(b);memiliki semantik yang sama persis di mana yang terakhir bekerja; setidaknya untuk semua jenis waras. Semua tipe standar waras dalam hal itu.

Kecuali jika Anda menggunakan pengalokasi yang menarik (artinya stateful, tidak selalu sama, dan tidak disebarkan pada swap kontainer, lihat std::allocator_traits), bertukar dua std::vectors dengan argumen templat yang sama hanyalah pertukaran membosankan dari tiga nilai (untuk kapasitas, ukuran, dan data- pointer). Dan bertukar tipe dasar, tidak ada perlombaan data, aman dan tidak bisa dilempar.

Itu bahkan dijamin oleh standar. Lihat std::vector::swap().

Deduplicator
sumber