Nauczyłem się konstruktorów ruchu w ciągu ostatniego dnia, starając się trzymać ogólnej zasady zwracania przez wartość, jak większość ludzi sugeruje, i natknąłem się na interesującą ja) dylemat.RVO, operacje przeniesienia i dylemat
Załóżmy, że mam kosztowne skonstruowanie/skopiowanie klasy "C", która poprawnie zdefiniowała konstruktora kopii, operatora przypisania, konstruktora ruchu i operatora przeniesienia.
pierwsze, część kodu elides konstruktora kopii jak Przewidywana
C make_c1() {
return C();
}
podobnie jak poniżej:
C make_c2() {
C tmp;
return tmp;
}
tak jak i w tym (czy przekazać w pozycji 1 lub 2) :
C make_c3(int a) {
return a == 1 ? make_c1() : make_c2();
}
to kiedy się do tego, że mam problem:
C make_c4(int a) {
C tmp;
return a == 1 ? make_c1() : tmp;
}
Podanie 1 powoduje wybranie RVO dla wyniku make_c1, ale podanie wartości 2 powoduje uruchomienie konstruktora kopiowania w tmp.
zmianie funkcji na następujące wywołuje konstruktor posunięcie być wyzwalany przez tmp Zamiast:
C make_c5(int a) {
C tmp;
return a == 1 ? make_c1() : std::move(tmp);
}
Wszystko wielki i wspaniały z wyjątkiem ...
W tych prostych przykładach RVO został wywołany dość tak jak się spodziewałem.
Co jednak, jeśli mój kod jest nieco bardziej złożony i na niektórych kompilatorach nie wywołuje RVO w tej ostatniej funkcji? W takim przypadku będę musiał zawinąć moje wywołanie make_c1 w std :: move, co sprawi, że kod będzie mniej wydajny na tych kompilatorach, które wywołują RVO.
Więc moje pytania to:
- Dlaczego konstruktor ruch nie powoływać w make_c4 kiedy wróciłem mój lokalny obiekt? (W końcu zostanie zniszczony).
- W funkcji make_c5, czy powinienem zwrócić wyniki make_c1 według wartości lub przesuwając je? (Aby uniknąć różnych wersji kodu dla różnych kompilatorów/platform).
- Czy istnieje lepszy sposób kodowania funkcji końcowej tak, aby działał prawidłowo w przypadku uzasadnionej implementacji kompilatora?
Kompilator, z którym grałem, to GCC 4.5.3 na Cygwin.
Nie jestem pewien, czy to odpowiada na wszystkie moje pytania, mimo że "naprawia" konkretny kod, z którym gram. Dlaczego twoja forma 2 zwrotów działa, gdy moja forma nie? Czy to jest problem z kompilacją? Sądzę, że częścią mojego problemu jest to, że RVO jest całkowicie zależne od kompilatora, podczas gdy poruszanie się jest pod moją kontrolą. –
@ IanM_Matrix1: nie powinieneś polegać na konkretnej optymalizacji, aby uzyskać krytyczny przyrost wydajności, ponieważ jest to tajemna nauka i nie można wiarygodnie przewidzieć, kiedy zostanie uruchomiona, czy nie. To właśnie nas "przenosi": zdolność kontrolowania w pewien sposób tego, co się dzieje. Jeśli twoja klasa jest droga do skopiowania, pomyślałabym o całkowitym wyłączeniu kopii, aby zapobiec przypadkowemu użyciu. –
Całkowicie się z tym zgadzam, ale czasami kopiowanie jest konieczne ** i ** kosztowne, chociaż funkcja typu klonowego dałaby lepszą kontrolę nad tym. W każdym razie wygląda na to, że na moje pytania odpowiedziano najnowszą aktualizacją Howarda - 1 odpowiada na ostatnią aktualizację, 2 odpowiada "nie", a 3 odpowiada oryginalnym kodem. Dzięki Howard :) –