Czy zwracanie przez odwołanie do wartości r jest bardziej wydajne?

137

na przykład:

Beta_ab&&
Beta::toAB() const {
    return move(Beta_ab(1, 1));
}
Neil G.
źródło

Odpowiedzi:

254
Beta_ab&&
Beta::toAB() const {
    return move(Beta_ab(1, 1));
}

To zwraca wiszące odniesienie, tak jak w przypadku odniesienia l-wartości. Po powrocie funkcji obiekt tymczasowy zostanie zniszczony. Powinieneś zwrócić Beta_abwedług wartości, jak poniżej

Beta_ab
Beta::toAB() const {
    return Beta_ab(1, 1);
}

Teraz poprawnie przenosi tymczasowy Beta_abobiekt do wartości zwracanej przez funkcję. Jeśli kompilator może, całkowicie uniknie tego ruchu, używając RVO (optymalizacja wartości zwracanej). Teraz możesz wykonać następujące czynności

Beta_ab ab = others.toAB();

I przeniesie konstrukcję tymczasową do ablub zrobi RVO, aby całkowicie pominąć wykonywanie ruchu lub kopiowania. Polecam przeczytanie BoostCon09 Rvalue References 101, które wyjaśniają sprawę i jak (N) RVO wchodzi z tym w interakcje.


Twój przypadek zwrócenia odwołania do wartości r byłby dobrym pomysłem w innych przypadkach. Wyobraź sobie, że masz getAB()funkcję, którą często wywołujesz tymczasowo. Nie jest optymalne, aby zwracał referencję const lvalue dla tymczasowych wartości r. Możesz to zaimplementować w ten sposób

struct Beta {
  Beta_ab ab;
  Beta_ab const& getAB() const& { return ab; }
  Beta_ab && getAB() && { return move(ab); }
};

Zauważ, że movew tym przypadku nie jest opcjonalna, ponieważ abnie jest ani lokalną automatyczną, ani tymczasową wartością r. Teraz kwalifikator ref && mówi, że druga funkcja jest wywoływana na tymczasowych rvalue, wykonując następujący ruch zamiast kopiowania

Beta_ab ab = Beta().getAB();
Johannes Schaub - litb
źródło
54
Zawsze zakładałem, że problem z wiszącymi referencjami zniknął automagicznie, gdy typ zwracany był odwołaniem do wartości r. Cieszę się, że to wyprostowałem, zanim mnie ugryzło. Miażdżące stosy robali są do niczego.
deft_code
31
:) Naprawdę, referencje r-wartości są „tylko referencjami”, tak jak referencje l-wartości. niczego nie kopiują ani nie przechowują.
Johannes Schaub - litb
9
co oznacza stała i kwalifikator elementu członkowskiego bardziej niż prosta stała?
galinette
4
+ 1-ed, ale zepsuty link: BoostCon09 Rvalue References 101
Siu Ching Pong -Asuka Kenji-
3
@galinette To są kwalifikatory ref .
Malcolm,
1

To może być bardziej skuteczny, na przykład, w nieco innym kontekście:

template <typename T>
T&& min_(T&& a, T &&b) {
    return std::move(a < b? a: b);
}

int main() {
   const std::string s = min_(std::string("A"), std::string("B"));
   fprintf(stderr, "min: %s\n", s.c_str());
   return 0;
}

Ciekawe jest to, że na moim komputerze clang++ -O3generuje 54 instrukcje dla powyższego kodu w porównaniu z 62 instrukcjami dla zwykłego std::min. Jednak wraz z -O0nim generuje 518 instrukcji dla powyższego kodu w porównaniu z 481 dla zwykłego std::min.

cud. mice
źródło
Twoja odpowiedź mnie zmyliła. Próbowałem podobnej (być może) wersji, ale się nie udało: ideone.com/4GyUbZ Czy możesz wyjaśnić dlaczego?
Deqing
Użyłeś odwołania do tymczasowego obiektu for(:)wi integrując z cofniętym obiektem. Poprawka: ideone.com/tQVOal
Wonder.mice
5
czy ta odpowiedź nie jest właściwie błędna? dla parametru szablonu T, T && nie jest odwołaniem do wartości r, ale raczej odniesieniem uniwersalnym iw takim przypadku powinniśmy zawsze wywoływać std :: forward <T>, a nie std :: move! Nie wspominając o tym, że ta odpowiedź jest bezpośrednio sprzeczna z tą, która została najwyżej oceniona powyżej.
xdavidliu
@xdavidliu ta odpowiedź jest wymyślnym przykładem tego, jak zwracanie przez rvalue może być bardziej wydajne. std::move()jest używany tylko jako wyraźna obsada, aby lepiej zilustrować ten punkt. To nie jest kod, który można skopiować i wkleić do projektu. Nie jest to sprzeczne z odpowiedzią z najwyższą liczbą głosów, ponieważ wewnątrz funkcji tworzony jest obiekt tymczasowy. Tutaj zwrócony obiekt jest jednym z argumentów (obiekty tymczasowe są niszczone jako ostatni krok w ocenie pełnego wyrażenia, które (leksykalnie) zawiera punkt, w którym zostały utworzone).
Wonder.mice
2
@ Wonder.mice, a następnie zamień T na std :: string; nie ma tutaj potrzeby używania szablonów, a używanie T && jako odniesienia do wartości r jest po prostu okropnym stylem, który niepotrzebnie dezorientuje ludzi, którzy nie znają szablonów i wartości-r.
xdavidliu