การสลับเวกเตอร์สองตัวที่แตกต่างกันใน C ++ ใช้วิธี std :: vector :: swap ปลอดภัยหรือไม่?


30

สมมติว่าคุณมีรหัสต่อไปนี้:

#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;
}

ลองนึกภาพเวกเตอร์ไม่ใช่std::stringยังคลาส:

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

มันปลอดภัยไหมที่จะสลับสองเวกเตอร์ด้วยstd::vector::swapวิธีการ: WidgetVector.swap(Widget2Vector);หรือมันจะนำไปสู่ ​​UB?

คำตอบ:


20

ปลอดภัยเพราะไม่มีอะไรถูกสร้างขึ้นระหว่างการดำเนินการสลับ std::vectorมีการสลับเฉพาะสมาชิกข้อมูลของคลาส

พิจารณาโปรแกรมสาธิตต่อไปนี้ซึ่งทำให้ชัดเจนว่าการแลกเปลี่ยนวัตถุของคลาสstd::vectorนั้นเป็นอย่างไร

#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;
}

ผลลัพธ์ของโปรแกรมคือ

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 

ในขณะที่คุณเห็นข้อมูลสมาชิกเท่านั้นptrและnถูกสลับในการแลกเปลี่ยนฟังก์ชั่นสมาชิก ไม่มีการใช้ทรัพยากรเพิ่มเติม

std::vectorวิธีการที่คล้ายกันถูกนำมาใช้ในชั้นเรียน

สำหรับตัวอย่างนี้

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

จากนั้นมีวัตถุของคลาสที่แตกต่างกัน ฟังก์ชั่นการแลกเปลี่ยนสมาชิกถูกนำไปใช้กับเวกเตอร์ประเภทเดียวกัน


6
แต่ในกรณีของ OP จริง ๆแล้วเวกเตอร์ที่ถูกสลับเป็นคลาสที่แตกต่างกันอย่างไร
Adrian Mole

4
@AdrianMole ฟังก์ชันสมาชิกสลับถ้ากำหนดไว้สำหรับประเภทที่กำหนดของเวกเตอร์ ไม่ได้กำหนดไว้สำหรับเวกเตอร์ประเภทต่าง ๆ ไม่ใช่ฟังก์ชันสมาชิกเทมเพลต
วลาดจากมอสโก

"swap ใช้กับเวกเตอร์ประเภทเดียวกัน" คุณควรเพิ่ม "only" ระหว่าง "is" และ "Apply"
SS Anne

แน่นอนผู้จัดสรรสถานะอาจเปลี่ยนสิ่งต่าง ๆ
Deduplicator

21

ใช่มันปลอดภัยอย่างสมบูรณ์ในการสลับเว็กเตอร์ประเภทเดียวกัน

เวกเตอร์ภายใต้ประทุนเป็นเพียงตัวชี้ไม่กี่ตัวชี้ไปยังข้อมูลที่ใช้เวกเตอร์และ "จบ" ของลำดับ เมื่อคุณเรียก swap คุณแค่แลกเปลี่ยนพอยน์เตอร์ระหว่างเวกเตอร์ คุณไม่ต้องกังวลว่าเวกเตอร์มีขนาดเท่ากันเพราะสิ่งนี้

swapเวกเตอร์ที่แตกต่างกันไม่สามารถสลับการใช้ คุณจะต้องใช้งานฟังก์ชั่นของคุณเองเพื่อทำการแปลงและแลกเปลี่ยน


3
คุณต้องดูอย่างละเอียดมากขึ้นในส่วนที่สองของคำถาม
Mark Ransom

@ MarkRansom Yep 2พลาด Updated
NathanOliver

13

การสลับเวกเตอร์สองตัวที่แตกต่างกันใน C ++ ใช้วิธี std :: vector :: swap ปลอดภัยหรือไม่?

ใช่. การแลกเปลี่ยนโดยทั่วไปถือว่าปลอดภัย ในอีกด้านหนึ่งความปลอดภัยนั้นเป็นแบบอัตนัยและสัมพันธ์กัน ดังนั้นจึงเป็นไปไม่ได้ที่จะให้คำตอบที่น่าพอใจโดยไม่ต้องเพิ่มคำถามด้วยบริบทและเลือกประเภทของความปลอดภัยที่กำลังพิจารณา

จะปลอดภัยไหมที่จะสลับสองเวกเตอร์ด้วยวิธี std :: vector :: swap: WidgetVector.swap (Widget2Vector); หรือมันจะนำไปสู่การ UB?

จะไม่มี UB ใช่มันยังปลอดภัยในแง่ที่ว่าโปรแกรมนั้นมีรูปแบบไม่ดี


7

ฟังก์ชั่นที่กำหนดไว้ดังต่อไปนี้:swap void swap( T& a, T& b );หมายเหตุ: นี่เป็นที่ที่ทั้งสองaและbมี (และมีจะ) ประเภทเดียวกัน (ไม่มีฟังก์ชั่นดังกล่าวที่ถูกกำหนดด้วยลายเซ็นนี้: void swap( T1& a, T2& b )เนื่องจากไม่มีเหตุผล!)

ในทำนองเดียวกันswap()ฟังก์ชั่นสมาชิกของstd::vectorชั้นถูกกำหนดไว้ดังต่อไปนี้:

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

ขณะนี้เนื่องจากไม่มีคำจำกัดความ 'เทียบเท่า' ที่มีการแทนที่เทมเพลต (ดูที่ความเชี่ยวชาญเฉพาะทางของฟังก์ชันเท็มเพลต ) สำหรับพารามิเตอร์ฟังก์ชัน (ซึ่งจะเป็นรูปแบบtemplate <typename T2> void swap(std::vector<T2>& other):) พารามิเตอร์นั้นจะต้องเป็นเวกเตอร์ประเภทเดียวกัน (เทมเพลต) คลาส 'การโทร' (นั่นคือจะต้องเป็นvector<T1>)

ของคุณ std::vector<Widget>และstd::vector<Widget2>เป็นสองประเภทที่แตกต่างกันดังนั้นการเรียกเพื่อswapไม่คอมไพล์ไม่ว่าคุณจะพยายามใช้ฟังก์ชันสมาชิกของวัตถุใด ๆ (ตามรหัสของคุณ) หรือใช้ความเชี่ยวชาญเฉพาะของstd::swap()ฟังก์ชั่นที่รับสองstd:vectorวัตถุเป็นพารามิเตอร์


8
std::vector::swapเป็นฟังก์ชั่นสมาชิกวิธีการที่จะเป็นความเชี่ยวชาญของฟังก์ชั่นแม่แบบยืนฟรี?
Aconcagua

1
ผลลัพธ์ที่ถูกต้องการใช้เหตุผลที่ผิด
NathanOliver

2
@AdrianMole แม้ว่าจะมีความเชี่ยวชาญเฉพาะทางstd::swapแต่นั่นไม่ใช่สิ่งที่ OP ใช้อยู่ เมื่อคุณทำFirst.swap(Second);คุณเรียกstd::vector::swapสิ่งที่แตกต่างจากstd::swap
NathanOliver

1
ขออภัยฉันไม่ดี ... ลิงก์ของคุณตรงไปที่เอกสารของ std :: specialization สำหรับเวกเตอร์ แต่นั่นก็แตกต่างจากฟังก์ชั่นสมาชิกซึ่งมีอยู่เช่นกันและใช้ในคำถาม (เท่านั้น)
Aconcagua

2
เหตุผลที่จริงก็คล้ายคลึง: สมาชิกถูกกำหนดเป็นvoid swap(std::vector& other)(เช่นstd::vector<T>), ไม่เป็นtemplate <typename U> void swap(std::vector<U>& other)(สมมติว่า T เป็นพารามิเตอร์ประเภทสำหรับเวกเตอร์ตัวเอง)
Aconcagua

4

คุณไม่สามารถสลับเวกเตอร์เป็นสองประเภทที่แตกต่างกัน แต่เป็นข้อผิดพลาดในการคอมไพล์แทนที่จะเป็น UB vector::swapยอมรับเฉพาะเวกเตอร์ประเภทเดียวกันและตัวจัดสรร

ไม่แน่ใจว่าจะใช้งานได้หรือไม่ แต่ถ้าคุณต้องการให้เวกเตอร์ที่มีWidget2s ถูกแปลงWidget s คุณสามารถลองได้

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

Widget2Widgetจะต้องย้ายจาก constructible


0

using std::swap; swap(a, b); และ a.swap(b);มีความหมายเดียวกันที่แน่นอนที่หลังทำงาน; อย่างน้อยสำหรับประเภทสติใด ๆ ทุกประเภทมาตรฐานมีเหตุผลในเรื่องนั้น

เว้นแต่คุณจะใช้ตัวจัดสรรที่น่าสนใจ (หมายถึงสภาวะไม่เสมอกันและไม่แพร่กระจายในการแลกเปลี่ยนคอนเทนเนอร์ดูstd::allocator_traits) สลับสองstd::vector s ด้วยเทมเพลตอาร์กิวเมนต์เดียวกันเป็นเพียงการแลกเปลี่ยนที่น่าเบื่อในสามค่า (สำหรับความจุขนาดและข้อมูล - ตัวชี้) และการสลับประเภทพื้นฐานการขาดข้อมูลการแข่งขันมีความปลอดภัยและไม่สามารถโยนได้

ที่รับประกันได้โดยมาตรฐาน std::vector::swap()ดู

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.