Std :: vector :: swap yöntemini kullanarak iki farklı vektörü C ++ 'da değiştirmek güvenli midir?


30

Aşağıdaki koda sahip olduğunuzu varsayalım:

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

Vektörün std::stringhenüz sınıf olmadığını hayal edin :

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

İki vektörü std::vector::swapyöntemle değiştirmek hala güvenli mi : WidgetVector.swap(Widget2Vector);yoksa bir UB'ye yol açacak mı?

Yanıtlar:


20

Güvenlidir, çünkü takas işlemi sırasında hiçbir şey yaratılmaz. Yalnızca sınıfın veri üyeleri std::vectordeğiştirilir.

Sınıftaki nesnelerin nasıl std::vectordeğiştirildiğini netleştiren aşağıdaki açıklayıcı programı düşünün .

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

Program çıktısı

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 

Gördüğünüz gibi yalnızca veri üyeleri ptrve nüye işlevi değiş tokuşunda yer değiştirirler. Her iki ek kaynak da kullanılmaz.

Sınıfta da benzer bir yaklaşım kullanılmaktadır std::vector.

Bu örneğe gelince

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

farklı sınıflardan nesneler var. Üye işlev değişimi aynı türdeki vektörlere uygulanır.


6
Fakat değiştirilen vektörlerin farklı sınıflardan olduğu OP'nin gerçek durumu nedir?
Adrian Mole

4
@AdrianMole Belirli bir vektör türü için tanımlanmışsa üye işlevi değiş tokuşu. Farklı tipteki vektörler için tanımlanmamıştır. Bir şablon üyesi işlevi değildir.
Moskova'dan Vlad

"takas aynı tür vektörlere uygulanır" "is" ile "uygulanmış" arasına "sadece" eklemelisiniz.
SS Anne

Tabii ki, durum belirleyicileri bazı şeyleri değiştirebilir.
Tekilleştirici

21

Evet, aynı türdeki vektörleri değiştirmek son derece güvenlidir.

Kaputun altındaki vektör, vektörün kullandığı verilere ve dizinin "ucuna" işaret eden birkaç işarettir. Takas aradığınızda, bu işaretçileri vektörler arasında değiştirirsiniz. Bu yüzden vektörlerin aynı boyutta olduğundan endişelenmenize gerek yok.

Farklı tipteki vektörler kullanılarak değiştirilemez swap. Dönüştürme ve değiştirme işlemini yapan kendi işlevinizi uygulamanız gerekir.


3
Sorunun ikinci kısmına daha yakından bakmanız gerekiyor.
Mark Ransom

@MarkRansom Yep. Cevapsız 2. Güncellenmiş.
NathanOliver

13

Std :: vector :: swap yöntemini kullanarak iki farklı vektörü C ++ 'da değiştirmek güvenli midir?

Evet. Değiştirme genellikle güvenli kabul edilebilir. Diğer yandan, güvenlik öznel ve görecelidir ve farklı açılardan düşünülebilir. Bu nedenle, soruyu bir bağlamla genişletmeden ve ne tür bir güvenliğin değerlendirileceğini seçmeden tatmin edici bir cevap vermek mümkün değildir.

İki vektörü std :: vector :: swap yöntemiyle değiştirmek hala güvenli mi: WidgetVector.swap (Widget2Vector); yoksa bir UB'ye mi yol açacak?

UB olmayacak. Evet, programın kötü biçimlendirilmiş olması hâlâ güvende.


7

swapAşağıdaki şekilde işlev tanımlanır: void swap( T& a, T& b );. Burada hem ave hem de aynı türb (ve olması gerekir) olduğunu unutmayın . (Bu imzayla tanımlanan böyle bir işlev yoktur: anlamsız olacağı için!)void swap( T1& a, T2& b )

Benzer şekilde, sınıfın swap()üye işlevi std::vectoraşağıdaki gibi tanımlanır:

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

Şimdi, işlev parametresi için (şu şekilde olabilir :) şablon geçersiz kılma özelliğine sahip 'eşdeğer' tanım olmadığından (bkz . İşlev şablonlarının açık uzmanlıklarıtemplate <typename T2> void swap(std::vector<T2>& other) ), bu parametre aynı türden (şablon) bir vektör olmalıdır 'çağıran' sınıf (yani a olmalıdır vector<T1>).

Sizin std::vector<Widget>ve std::vector<Widget2>iki farklı türdür, bu nedenle, swapnesnenin üye işlevini (kodunuzun yaptığı gibi) kullanmaya çalışsanız da veya iki nesneyi parametre olarak alan işlevin uzmanlaşmasını kullansanız da, derleme çağrısı derlenmez .std::swap()std:vector


8
std::vector::swapüye işlevi, bu nasıl bağımsız bir şablon işlevi bir uzmanlık olabilir ???
Aconcagua

1
Doğru sonuç, yanlış akıl yürütme.
NathanOliver

2
@AdrianMole Bir uzmanlaşma olsa da std::swap, OP'nin kullandığı bu değildir. Bunu yaptığınızda First.swap(Second);, std::vector::swaphangisinden farklı bir işlev olduğunu std::swap
görürsünüz

1
Üzgünüm, benim kötüyüm ... Bağlantı doğrudan std :: swap uzmanlık belgelerine gidiyor. Ancak bu , var olan ve söz konusu (yalnızca) kullanılan üye işlevinden farklıdır .
Aconcagua

2
Akıl yürütme aslında benzerdir: Üye (T'nin vektörün kendisi için tip parametresi olduğu varsayılarak ) olarak değil void swap(std::vector& other)(yani std::vector<T>) olarak tanımlanır template <typename U> void swap(std::vector<U>& other).
Aconcagua

4

İki farklı türdeki vektörleri değiştiremezsiniz, ancak bu UB yerine bir derleme hatasıdır. vector::swapsadece aynı tip ve ayırıcıdaki vektörleri kabul eder.

Bunun işe yarayıp yaramayacağından emin değilim, ancak Widget2s'den dönüştürülmüş Widgets içeren bir vektör istiyorsanız bunu deneyebilirsiniz:

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

Widget2... 'dan inşa edilebilir olmalı Widget.


0

using std::swap; swap(a, b);ve a.swap(b);ikincisinin çalıştığı semantikle tam olarak aynı semantiğe sahip olmak; en azından aklı başında herhangi bir tür için. Tüm standart tipler bu konuda aklı başında.

İlginç bir ayırıcı kullanmıyorsanız (durum bilgisi olan, her zaman eşit olmayan ve kap değişiminde yayılmadığı sürece, bkz. std::allocator_traits), İki std::vectorşablonu aynı şablon bağımsız değişkenleriyle değiştirmek, yalnızca üç değerin (kapasite, boyut ve veri- Işaretçi). Ve veri türlerinin bulunmadığı temel türleri değiştirmek güvenlidir ve atamaz.

Bu standart tarafından bile garanti edilir. Bkz std::vector::swap().

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.