Zdaję sobie sprawę z 5 ogólnych kategorii, w których rekompilacja kompilatora C ++ 03 jako C ++ 11 może powodować nieograniczony wzrost wydajności, który jest praktycznie niezwiązany z jakością implementacji. Są to wszystkie odmiany semantyki ruchu.
std::vector
zmienić przydział
struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
każdym razem, gdy foo
bufor „s jest przesunięte w C ++ 03 jest kopiowany każdy vector
w bar
.
W C ++ 11 zamiast tego przenosi bar::data
s, który jest w zasadzie darmowy.
W tym przypadku zależy to od optymalizacji wewnątrz std
kontenera vector
. W każdym przypadku poniżej użycie std
kontenerów wynika tylko z tego, że są to obiekty C ++, które mają wydajną move
semantykę w C ++ 11 „automatycznie” podczas aktualizacji kompilatora. Obiekty, które go nie blokują i które zawierają std
kontener, również dziedziczą automatycznie ulepszone move
konstruktory.
Awaria NRVO
Kiedy NRVO (optymalizacja nazw zwracanych wartości) zawiedzie, w C ++ 03 wraca do kopii, w C ++ 11 wraca do ruchu. Awarie NRVO są łatwe:
std::vector<int> foo(int count){
std::vector<int> v; // oops
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
lub nawet:
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
Mamy trzy wartości - wartość zwracaną i dwie różne wartości w funkcji. Elision pozwala „scalić” wartości w funkcji z wartością zwracaną, ale nie ze sobą. Oba nie mogą zostać połączone z wartością zwracaną bez połączenia ze sobą.
Podstawową kwestią jest to, że wybór NRVO jest kruchy, a kod ze zmianami, które nie znajdują się w pobliżu return
witryny, może nagle spowodować znaczne obniżenie wydajności w tym miejscu bez emitowania diagnostyki. W większości przypadków awarii NRVO C ++ 11 kończy się na a move
, podczas gdy C ++ 03 kończy się na kopii.
Zwracanie argumentu funkcji
Wykluczenie jest również tutaj niemożliwe:
std::set<int> func(std::set<int> in){
return in;
}
w C ++ 11 jest to tanie: w C ++ 03 nie ma możliwości uniknięcia kopiowania. Argumenty funkcji nie mogą być pomijane za pomocą wartości zwracanej, ponieważ czasem życia i lokalizacją parametru i wartości zwracanej zarządza kod wywołujący.
Jednak C ++ 11 może przenosić się z jednego do drugiego. (W mniej zabawkowym przykładzie coś można zrobić z set
).
push_back
lub insert
Wreszcie nie następuje elucja do kontenerów: ale C ++ 11 przeciąża operatorów wstawiania wartości rvalue, co zapisuje kopie.
struct whatever {
std::string data;
int count;
whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );
w C ++ 03 whatever
tworzony jest plik tymczasowy , a następnie kopiowany do wektora v
. std::string
Przydzielono 2 bufory, każdy z identycznymi danymi, a jeden odrzucono.
W C ++ 11 whatever
tworzony jest tymczasowy . whatever&&
push_back
Przeciążenie następnie move
y, że tymczasowa do wektora v
. Jeden std::string
bufor jest przydzielany i przenoszony do wektora. Pusty std::string
jest odrzucany.
Zadanie
Skradzione z odpowiedzi @ Jarod42 poniżej.
Wykluczenie nie może nastąpić wraz z przypisaniem, ale przejście może.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
tutaj some_function
zwraca kandydata do ucieczki, ale ponieważ nie jest on używany do bezpośredniej budowy obiektu, nie można go uciec. W C ++ 03 powyższe powoduje skopiowanie zawartości tymczasowej some_value
. W C ++ 11 jest przeniesiony do some_value
, który w zasadzie jest darmowy.
Aby uzyskać pełny efekt powyższego, potrzebujesz kompilatora, który syntetyzuje dla ciebie konstruktory ruchów i przypisania.
MSVC 2013 implementuje konstruktory ruchu w std
kontenerach, ale nie syntezuje konstruktorów ruchu na twoich typach.
Tak więc typy zawierające std::vector
si i podobne nie uzyskują takich ulepszeń w MSVC2013, ale zaczną je otrzymywać w MSVC2015.
clang i gcc już dawno zaimplementowały niejawne konstruktory ruchów. Kompilator Intela 2013 będzie obsługiwał niejawne generowanie konstruktorów ruchów, jeśli zdasz -Qoption,cpp,--gen_move_operations
(domyślnie nie robią tego, aby być kompatybilnym z MSVC2013).