Żywotność tymczasowych obiektów powiązanych z odniesieniami zostaje przedłużona, chyba że istnieje wyjątek. Oznacza to, że jeśli nie ma takiego wyjątku, wówczas żywotność zostanie przedłużona.
Od dość niedawnym projekcie, N4567:
Drugi kontekst [gdzie życie jest rozszerzony] jest, gdy odniesienia związany z tymczasowym. Tymczasowego, do którego odniesienie jest związany lub tymczasowe pełna przedmiotem podobiektu do których odniesienia związanego utrzymuje się przez cały czas trwania odniesienie wyjątkiem:
- (5.1) tymczasowego obiektu związanego do parametru odniesienia w wywołaniu funkcji (5.2.2) trwa do zakończenia pełnego wyrażenia zawierającego połączenie.
- (5.2) Czas życia tymczasowego powiązania z wartością zwróconą w instrukcji return funkcji (6.6.3) nie jest przedłużany; tymczasowy jest zniszczony na końcu pełnego wyrażenia w instrukcji return.
- (5.3) Czasowe powiązanie z odwołaniem w nowym inicjatorze (5.3.4) trwa do momentu zakończenia pełnego wyrażenia zawierającego nowy inicjator.
Jedyna istotna zmiana C++ 11 jest, jak wspomniano PO, że C++ 11 znajduje się dodatkowa wyjątkiem członków rodzajów danych referencyjnych (z N3337):
- Ograniczenie tymczasowe do elementu referencyjnego w inicjatorze ctor konstruktora (12.6.2) trwa do momentu zakończenia konstruktora.
została usunięta, w CWG 1696 (po-C++ 14) i tymczasowe wiązanie obiektów odniesienia elementów danych przez MEM-inicjator jest słabo formowane.
Odnośnie przykładów w PO:
struct S
{
const std::string& str_;
};
S a{"foo"}; // direct-initialization
ta tworzy tymczasowy std::string
i inicjuje człon str_
danych z niego. S a{"foo"}
używa inicjowania agregacji, więc nie ma w tym celu inicjatora pamięci. Nie ma zastosowania żaden wyjątek dotyczący przedłużenia okresu ważności, dlatego okres ważności tego tymczasowego okresu jest przedłużany do okresu użytkowania elementu danych odniesienia str_
.
auto b = S{"bar"}; // copy-initialization with rvalue
Przed obowiązkową kopiowania elizji z C++ 17: Formalnie możemy utworzyć tymczasowy std::string
zainicjować tymczasowej S
poprzez wiązanie tymczasowy std::string
członkowi str_
referencyjnej . Następnie przenosimy ten tymczasowy kod do S
w b
. Spowoduje to "skopiowanie" odniesienia, które nie wydłuży czasu życia tymczasowego. Jednak implementacje spowodują przeniesienie z tymczasowego adresu URL do S
do b
. Nie może to jednak wpływać na czas życia tymczasowego. Można zaobserwować to w następującym programie:
#include <iostream>
#define PRINT_FUNC() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
struct loud
{
loud() PRINT_FUNC()
loud(loud const&) PRINT_FUNC()
loud(loud&&) PRINT_FUNC()
~loud() PRINT_FUNC()
};
struct aggr
{
loud const& l;
~aggr() PRINT_FUNC()
};
int main() {
auto x = aggr{loud{}};
std::cout << "end of main\n";
(void)x;
}
Live demo
Zauważ, że destruktor z loud
nazywa się przed „końcem główne”, natomiast x
życie dopiero po tym śladu. Formalnie tymczasowy loud
jest niszczony na końcu pełnego wyrażenia, które go utworzyło.
Zachowanie się nie zmienia, jeśli konstruktor ruchu aggr
jest zdefiniowany przez użytkownika.
Z obowiązkowego kopiowaniem elizji w C++ 17: Identyfikujemy obiekt na RHS S{"bar"}
z obiektu na LHS b
. Powoduje to wydłużenie czasu trwania tymczasowego do czasu życia b
. Zobacz CWG 1697.
Dla pozostałych dwóch przykładów konstruktor ruchu - jeśli jest wywołany - po prostu kopiuje odniesienie. Konstruktor ruchu (z S
) może być oczywiście usunięty, ale nie można tego zaobserwować, ponieważ kopiuje tylko odniesienie.
Nie ma wyjątków od przedłużenia żywotności modułów tymczasowych dla inicjowania agregacji, dlatego okres ważności tymczasowego zostanie wydłużony. Gwarantuje to odpowiedni czas życia tymczasowego utworzonego w przypadku "bezpośredniego inicjowania". – dyp
co masz na myśli mówiąc "ruch może zostać usunięty"? Wiązanie referencji nie może zostać usunięte, 'str_' wiąże się bezpośrednio z' other.str'. ('std :: move' nie ma żadnego efektu) –
@ M.M Mam na myśli, że większość kompilatorów będzie wykonywać bezpośrednią inicjalizację, zamiast używać konstruktora ruchu. Tak 'std :: move (inne) .str' jest takie samo jak' other.str' dla referencji i nie ma tutaj żadnego efektu – 3XX0