2009-07-28 18 views
7

Mam funkcję, która zwraca obiekt typu Foo:Przypisywanie wynik funkcji, która zwraca Foo do const Foo &

Foo getFoo(); 

wiem co następuje będzie skompilować i będzie działać, ale dlaczego ja kiedykolwiek to robisz?

const Foo& myFoo = getFoo(); 

Dla mnie po to dużo bardziej czytelne, a nie zmuszać mnie, aby pamiętać, że C++ pozwala mi przypisać wartość R do const odniesienia:

const Foo myFoo = getFoo(); 

Jakie są różnice między tymi dwoma? Dlaczego miałbym używać pierwszego przez sekundę? Dlaczego miałbym używać drugiego w ciągu pierwszego?

Odpowiedz

4

wbrew powszechnej opinii, nie ma gwarancji, że przypisywanie wynik funkcję powracającego obiektu przez wartość dla const odniesienia spowoduje mniej egzemplarzy niż przypisanie go do obiektu samo.

Po przypisaniu wartości r do odwołania do const, kompilator może powiązać odwołanie na jeden z dwóch sposobów Może utworzyć nowy tymczasowy, kopiując wartość rurn i powiązać z nią odniesienie, lub może powiązać odniesienie bezpośrednio z samą wartością runk.

Jeśli kompilator nie jest w stanie wykonać "oczywistej" optymalizacji w celu usunięcia tymczasowego i przeniesienia konstruktora kopiowania dla wartości zwracanej getFoo, to jakie jest prawdopodobieństwo, że będzie w stanie wykonać bardziej wydajną formę wiązania rwartość do wartości stałej bez tworzenia nowego tymczasowego?

Jednym z powodów użycia referencji do stałych byłoby sprawienie, że funkcja będzie bardziej odporna na potencjalne cięcie.Jeśli typem zwracanym był typ pochodzący od Foo, to zagwarantowanie, że nie zostanie podzielone odwołanie do referencji stałej dla klasy bazowej, nawet jeśli kompilator wykona obiekt tymczasowy z wartości zwracanej przez funkcję. Kompilator wygeneruje również poprawne wywołanie do destruktora klasy pochodnej , niezależnie od tego, czy destruktor w klasie bazowej jest wirtualny, czy też nie, czy nie jest nim. Dzieje się tak dlatego, że typ utworzonego tymczasowego obiektu jest oparty na typie przypisywanego wyrażenia, a nie na typie inicjowanego odniesienia.

Należy zauważyć, że kwestia tego, jak wiele kopii zwracanej wartości są dokonywane jest całkowicie oddzielony od optymalizacji wartości powrotnej i optymalizacja wartość nazwie powrót. Te optymalizacje odnoszą się do wyeliminowania kopii wyniku rwartości z wartościowania zwrotnego lub nazwanej zmiennej lokalnej do wartości zwracanej funkcji w treści samej funkcji. Oczywiście w najlepszym możliwym przypadku można dokonać optymalizacji wartości zwracanej, a tymczasowy dla wartości zwracanej można wyeliminować, co powoduje, że nie są wykonywane żadne kopie zwróconego obiektu.

+0

Jeśli kompilator powiąza wartość rurnu z odwołaniem do const przez wykonanie kopii, która może obejmować konstruktor kopii, który jest zdefiniowany przez A :: A (const A &), w jaki sposób następowałby poniższy kod? A x = A (parametr); –

+0

@ZheYang: z notatki w starym standardzie: "Oczywiście, jeśli przetwarzana inicjalizacja odwołania jest jedna dla pierwszego argumentu wywołania konstruktora kopiowania, implementacja musi ostatecznie wybrać pierwszą alternatywę (powiązanie bez kopiowania), aby uniknąć nieskończonej rekurencji ". W nowym standardzie ten wybór został usunięty z implementacji. –

+0

Interesujące. Myślę, że to dlatego, że w nowym standardzie mamy teraz konstruktor ruchu, ale w każdym razie może go brakować. –

1

Jest ważne, aby umożliwić tego rodzaju wzoru:

void foo(const SomeType &); 

foo(SomeType(bar)) 

w tym przypadku tymczasowy SomeType jest skonstruowane i przekazywane do foo. Najprawdopodobniej fakt, że możesz również mieć stałe odniesienia na stosie do tymczasowych, jest efektem ubocznym słownictwa używanego do definiowania tego zachowania w standardzie. Zauważ, że jak wspomniano w komentarzach, czas życia tymczasowego został przedłużony do samego odniesienia.

+0

Więc nie ma dobrych powodów, aby zrobić "const Foo & myFoo = getFoo()'? –

+0

Jakie jest to pytanie próbujące odpowiedzi? –

+0

"możesz także mieć odniesienia do tymczasowych, które przeżywają same tymczasowe". Nie tak, nie możesz. W drugim wierszu kodu pytanie tymczasowe zwrócone przez funkcję getFoo() nie jest niszczone, dopóki 'myFoo' nie wykracza poza zakres. http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ –

0

Nie może być kilka powodów:

co, jeśli nie chcesz const przedmiot, o którym mowa? Na przykład to nie będzie działać:

 
    const Foo &myFoo = getFoo(); 
    myFoo.myfield = x; 

Albo, co jeśli wracają obiekt temp od getFoo()? To ostrzec o powrocie odniesienie (lub adres) do lokalnego:

 
    const Foo &getFoo(void) 
    { 
     Foo localFoo(); 
     // do the things you want to localFoo 
     return(localFoo); 
    } 

elementy wewnętrzne z const Foo& myFoo = getFoo() zrobić prawie to samo, co Foo myFoo = getFoo() więc argument, że istnieje stosunek wydajności do const zamian sygn są nieważne . Uważam, że nie ma problemu z zwracaniem przedmiotów o rozsądnej wielkości.

Zastrzeżenie - nie wypróbowałem tych przykładów na gcc. Twój przebieg może się odpowiednio różnić.

+0

Fakt, że nie możesz robić rzeczy niestałych na 'myFoo 'stosuje się niezależnie od tego, czy przechowujesz zwracaną wartość' getFoo() 'w' const Foo & 'lub' const Foo'. Ponadto mówię o 'getFoo()' zwracającym 'const Foo' zamiast' const Foo & '. –

Powiązane problemy