2013-03-31 10 views
8

danej klasy X poniżej (funkcje specjalny członkowskim innym niż państwo, wyraźnie zdefiniowanego nie są istotne dla tego eksperymentu):Dlaczego funkcja resize() powoduje kopiowanie, a nie przenoszenie zawartości wektora po przekroczeniu pojemności?

struct X 
{ 
    X() { } 
    X(int) { } 
    X(X const&) { std::cout << "X(X const&)" << std::endl; } 
    X(X&&) { std::cout << "X(X&&)" << std::endl; } 
}; 

Poniższy program tworzy wektor obiektów typu X i zmienia rozmiar tak, że jego pojemność jest przekroczony i realokacja jest zmuszony:

#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<X> v(5); 
    v.resize(v.capacity() + 1); 
} 

Ponieważ klasa X zapewnia konstruktor poruszać, spodziewałbym poprzednią zawartość wektora być przeniósł do t on nowy magazyn po realokacji. Dość nieoczekiwanie, that does not seem to be the case, a wyjście pojawia się:

X(X const&) 
X(X const&) 
X(X const&) 
X(X const&) 
X(X const&) 

Dlaczego?

+0

Należy również pamiętać, że _Microsoft_ nie respektuje tej reguły, więc ten kod skompilowany z _Visual C++ _ (lub _clang_ na _Windows_) wywoła konstruktor ruchu i tym samym może zostawić cię z uszkodzonymi (pustymi) elementami w 'std :: vector 'w przypadku błędu zmiany rozmiaru. – Denis

Odpowiedz

17

Punkt 23.3.6.3/14 z C++ 11 Określono (o funkcji resize() użytkownik szablonu vector<> klasa):

Uwagi: jeśli jest wyjątek inne niż Konstruktor ruchu nie-CopyInsertable Tnie ma efektów.

Innymi słowy, oznacza to, że dla X (co jest CopyInsertable) resize() oferuje strong guarantee: to albo się uda albo pozostawia stan wektora niezmienionej.

Aby spełnić tę gwarancję, implementacje przyjmują na przykład copy-and-swap idiom: jeśli konstruktor kopiowania z X rzuca, nie zmieniliśmy jeszcze zawartości oryginalnego wektora, więc dotrzymujemy obietnicy.

Jednakże, jeżeli poprzednia zawartość wektora były przeniósł w nowym magazynie zamiast być kopiowane i konstruktor ruch rzucił, to musielibyśmy nieodwracalnie zmienił oryginalną treść wektora.

Dlatego implementacje użyje konstruktora kopii X aby bezpiecznie przenieść zawartość wektora w nowym magazynie chyba konstruktor ruch jest znany nie rzucać, w tym przypadku jest to bezpieczne przejście z poprzednich elementów .

Z małą zmianą definicji konstruktora przenieść X „s (oznaczenie go jako noexcept), w rzeczywistości the output of the program is now the expected one.

struct X 
{ 
    X() { } 
    X(int) { } 
    X(X const&) { std::cout << "X(X const&)" << std::endl; } 
    X(X&&) noexcept { std::cout << "X(X&&)" << std::endl; } 
//   ^^^^^^^^ 
}; 
+1

Co się dzieje - pytanie na blogu? :-) –

+0

@KerrekSB: Powiedziano mi, że pytania, na które można odpowiedzieć, są w porządku, myślałem, że to będzie interesujące :) –

+0

@AndyProwl - Widziałem, że ktoś inny próbuje to zrobić i zostali pobici ... –

5

Pomyśl o gwarancji Wyjątek: Jeśli istnieje wyjątek podczas realokacji wektor musi pozostać niezmieniony. Można to zagwarantować tylko przez skopiowanie elementów i zachowanie starego zestawu, dopóki cała kopia się nie powiedzie.

Tylko jeśli wiesz, że konstruktor ruchu nie rzuca, możesz bezpiecznie przenieść elementy do nowej lokalizacji. Aby to osiągnąć, zadeklaruj konstruktor ruchu noexcept.

Powiązane problemy