Dlaczego niedozwolone jest uzyskiwanie nie stałych odniesień do obiektu tymczasowego, który funkcja getx()
zwraca? Oczywiście jest to zabronione przez C ++ Standard, ale interesuje mnie cel takiego ograniczenia, a nie odniesienie do standardu.
struct X
{
X& ref() { return *this; }
};
X getx() { return X();}
void g(X & x) {}
int f()
{
const X& x = getx(); // OK
X& x = getx(); // error
X& x = getx().ref(); // OK
g(getx()); //error
g(getx().ref()); //OK
return 0;
}
- Oczywiste jest, że żywotność obiektu nie może być przyczyną, ponieważ stałe odwołanie do obiektu nie jest zabronione przez C ++ Standard.
- Oczywiste jest, że obiekt tymczasowy nie jest stały w powyższej próbce, ponieważ dozwolone są wywołania funkcji niestałych. Na przykład
ref()
może zmodyfikować obiekt tymczasowy. - Ponadto
ref()
pozwala oszukać kompilator i uzyskać link do tego obiektu tymczasowego, co rozwiązuje nasz problem.
Dodatkowo:
Mówią: „przypisanie tymczasowego obiektu do stałej referencji przedłuża jego żywotność” i „Nic nie mówi się o referencjach nie stałych”. Moje dodatkowe pytanie . Czy następujące przypisanie przedłuża żywotność obiektu tymczasowego?
X& x = getx().ref(); // OK
Odpowiedzi:
Z tego artykułu na blogu Visual C ++ o referencjach wartości :
Zasadniczo nie powinieneś próbować modyfikować tymczasowych z tego samego powodu, że są to tymczasowe obiekty i umrą teraz. Powodem, dla którego możesz wywoływać metody non-const, jest to, że, no cóż, możesz robić pewne „głupie” rzeczy, o ile wiesz, co robisz i wyrażasz o tym wyraźne zdanie (np. Używając reinterpret_cast). Ale jeśli powiążesz tymczasowe odniesienie z non-const, możesz nadal przesyłać je „na zawsze” tylko po to, aby zniknąć manipulacja obiektem, ponieważ gdzieś po drodze całkowicie zapomniałeś, że to tymczasowe.
Gdybym był tobą, przemyślałbym projekt moich funkcji. Dlaczego g () akceptuje referencje, czy modyfikuje parametr? Jeśli nie, ustaw jako stałe odniesienie, jeśli tak, dlaczego próbujesz przekazać to tymczasowo, czy nie obchodzi cię to, że modyfikujesz tymczasowo? Dlaczego mimo to getx () zwraca tymczasowe? Jeśli podzielisz się z nami swoim prawdziwym scenariuszem i tym, co próbujesz osiągnąć, możesz uzyskać kilka dobrych wskazówek, jak to zrobić.
Sprzeciwianie się językowi i oszukiwanie kompilatora rzadko rozwiązuje problemy - zwykle stwarza problemy.
Edycja: Odpowiadanie na pytania w komentarzu: 1)
X& x = getx().ref(); // OK when will x die?
- Nie wiem i nie obchodzi mnie to, ponieważ właśnie to mam na myśli, mówiąc „przeciw językowi”. Język mówi „tymczasowe umierają na końcu instrukcji, chyba że są zobowiązane do stałej referencji, w którym to przypadku umierają, gdy referencja wykracza poza zakres”. Stosując tę regułę, wydaje się, że x jest już martwy na początku następnej instrukcji, ponieważ nie jest związany z const referen (kompilator nie wie, co zwraca ref ()). To tylko przypuszczenie.2) Jasno określiłem cel: nie wolno modyfikować tymczasowości, ponieważ to po prostu nie ma sensu (ignorowanie odniesień do wartości C ++ 0x). Pytanie „dlaczego wolno mi dzwonić do członków spoza grupy?” jest dobra, ale nie mam lepszej odpowiedzi niż ta, którą już powiedziałem powyżej.
3) Cóż, jeśli mam rację co do x
X& x = getx().ref();
śmierci na końcu oświadczenia, problemy są oczywiste.W każdym razie, w oparciu o twoje pytanie i komentarze, nie sądzę, że nawet te dodatkowe odpowiedzi cię zadowolą. Oto ostatnia próba / streszczenie: komisja C ++ zdecydowała, że nie ma sensu modyfikować tymczasowości, dlatego nie zezwoliła na wiązanie z odwołaniami innymi niż stałe. Może to dotyczyć implementacji kompilatora lub dotyczyły również problemów historycznych, nie wiem. Potem pojawił się pewien konkretny przypadek i zdecydowano, że wbrew wszelkim przeciwnościom nadal będą one mogły bezpośrednio modyfikować poprzez wywołanie metody non-const. Ale to wyjątek - generalnie nie wolno modyfikować tymczasowych. Tak, C ++ jest często tak dziwny.
źródło
int
itd), ale jest to dozwolone dla typów zdefiniowanych przez użytkownika:(std::string("A")+"B").append("C")
.g()
zmodyfikuje obiekt (czego można oczekiwać od funkcji przyjmującej odwołanie inne niż const), zmodyfikowałby obiekt, który umrze, aby nikt nie mógł uzyskać zmodyfikowanej wartości. Mówi, że najprawdopodobniej jest to błąd.W kodzie
getx()
zwracany jest obiekt tymczasowy, tzw. „Rvalue”. Możesz skopiować wartości do obiektów (zwanych także zmiennymi) lub powiązać je z stałymi referencjami (co wydłuży ich żywotność do końca życia referencji). Nie można powiązać wartości z referencjami innymi niż const.To była celowa decyzja projektowa, aby zapobiec przypadkowej modyfikacji obiektu, który umrze na końcu wyrażenia:
Jeśli chcesz to zrobić, musisz najpierw wykonać kopię lokalną lub obiektu albo powiązać ją z odwołaniem do stałej:
Zauważ, że następny standard C ++ będzie zawierał odniesienia do wartości. To, co znacie jako referencje, staje się zatem nazywane „referencją do wartości”. Będziesz mógł powiązać wartości z referencjami do wartości i możesz przeciążać funkcje na „wartości-wartości”:
Ideą referencji do wartości jest to, że skoro te obiekty i tak umrą, możesz skorzystać z tej wiedzy i wdrożyć tak zwaną „semantykę ruchu”, pewien rodzaj optymalizacji:
źródło
g(getx())
nie działa, ponieważ jego sygnatura jestg(X& x)
iget(x)
zwraca obiekt tymczasowy, więc nie możemy powiązać obiektu tymczasowego ( wartości ) z niestałym odwołaniem, prawda? I w twoim pierwszym fragmencie kodu, myślę, że będzie toconst X& x2 = getx();
zamiastconst X& x1 = getx();
…:-/
Tak, twoje rozumowanie jest poprawne, choć trochę wstecz: nie możemy powiązać tymczasowych odniesień z wartościami innymi niżconst
(wartość), a zatem tymczasowe zwrócone przezgetx()
(i nieget(x)
) nie mogą być powiązane z odwołaniem do wartości, do której jest argumentemg()
.getx()
(i nieget(x)
) ?getx()
, a nieget(x)
(jak pisałeś).Pokazujesz, że dozwolone jest tworzenie łańcuchów operatorów.
Wyrażenie to „getx (). Ref ();” i jest to wykonywane do końca przed przypisaniem do „x”.
Zauważ, że getx () nie zwraca referencji, ale w pełni ukształtowany obiekt w lokalnym kontekście. Obiekt jest chwilowa, ale to nie const, co pozwala na łączenie się z innych metod obliczania wartości lub mają inne skutki uboczne zdarzają.
Spójrz na koniec tej odpowiedzi, aby uzyskać lepszy przykład tego
Można nie wiążą tymczasowy do odniesienia, ponieważ spowoduje to wygenerowanie odniesienie do obiektu, który zostanie zniszczony na końcu wyrażenia w ten sposób pozostawiając cię z wiszącym odniesienia (który jest zaniedbany, a średnia nie jak niechlujny).
Wartość zwrócona przez ref () jest prawidłowym odwołaniem, ale metoda nie zwraca uwagi na żywotność zwracanego obiektu (ponieważ nie może mieć tej informacji w swoim kontekście). Zasadniczo właśnie wykonałeś odpowiednik:
Powodem, dla którego można to zrobić w przypadku stałego odwołania do obiektu tymczasowego, jest to, że standard wydłuża czas życia obiektu tymczasowego do okresu życia odwołania, tak że czas życia obiektów tymczasowych jest przedłużony poza koniec instrukcji.
Pozostaje więc tylko pytanie, dlaczego standard nie zezwala na odwołanie do tymczasowości, aby przedłużyć żywotność obiektu poza koniec instrukcji?
Uważam, że dzieje się tak, ponieważ spowodowałoby to, że kompilator bardzo utrudniłby poprawienie tymczasowych obiektów. Zostało to zrobione dla stałych odwołań do plików tymczasowych, ponieważ ma to ograniczone użycie, a zatem zmusiło cię do zrobienia kopii obiektu, aby zrobić cokolwiek użytecznego, ale zapewnia pewne ograniczone funkcje.
Pomyśl o tej sytuacji:
Wydłużenie żywotności tego tymczasowego obiektu będzie bardzo mylące.
Podczas gdy następujące:
Daje ci kod, który jest intuicyjny w obsłudze i zrozumieniu.
Jeśli chcesz zmodyfikować wartość, powinieneś zwrócić wartość do zmiennej. Jeśli próbujesz uniknąć kosztu skopiowania obiektu z powrotem z funkcji (wydaje się, że obiekt jest kopią skonstruowaną z powrotem (technicznie jest)). Więc nie przejmuj się, kompilator jest bardzo dobry w „Optymalizacji wartości zwrotu”
źródło
const_cast<x&>(getX());
nie ma sensuDlaczego omówiono w C ++ FAQ ( pogrubiona kopalnia):
źródło
x * 0
dajex
? Co? Co??incr(0);
w taki sposób. Oczywiście, gdyby było to dozwolone, byłoby to interpretowane jako utworzenie tymczasowej liczby całkowitej i przekazanie jej doincr()
Głównym problemem jest to
jest logicznym błędem:
g
modyfikuje wynik,getx()
ale nie masz szans na sprawdzenie zmodyfikowanego obiektu. Gdybyg
nie musiał modyfikować swojego parametru, nie wymagałby odwołania do wartości, mógłby wziąć parametr według wartości lub stałej.jest poprawna, ponieważ czasami trzeba ponownie użyć wyniku wyrażenia i jest całkiem jasne, że masz do czynienia z obiektem tymczasowym.
Jednak nie można tego zrobić
ważne bez
g(getx())
unieważniania, czego projektanci języków starali się przede wszystkim unikać.jest poprawny, ponieważ metody wiedzą tylko o
this
stałości parametru , nie wiedzą, czy są wywoływane na wartości lvalue czy rvalue.Jak zawsze w C ++, masz obejście tej reguły, ale musisz zasygnalizować kompilatorowi, że wiesz, co robisz, jawnie:
źródło
Wydaje się, że na pierwotne pytanie, dlaczego nie jest to dozwolone, udzielono jasnej odpowiedzi: „ponieważ najprawdopodobniej jest to błąd”.
FWIW, myślałem, że pokażę, jak to zrobić, chociaż nie sądzę, że to dobra technika.
Powodem, dla którego czasami chcę przekazać tymczasową metodę przyjmującą odwołanie inne niż stałe, jest celowe odrzucenie wartości zwracanej przez odwołanie, o którą nie obchodzi metoda wywołująca. Coś takiego:
Jak wyjaśniono w poprzednich odpowiedziach, to się nie kompiluje. Ale to kompiluje się i działa poprawnie (z moim kompilatorem):
To tylko pokazuje, że możesz użyć rzutowania, by okłamać kompilator. Oczywiście znacznie łatwiej byłoby zadeklarować i przekazać nieużywaną zmienną automatyczną:
Ta technika wprowadza niepotrzebną zmienną lokalną do zakresu metody. Jeśli z jakiegoś powodu chcesz uniemożliwić użycie go później w metodzie, np. Aby uniknąć pomyłek lub błędów, możesz ukryć go w lokalnym bloku:
- Chris
źródło
Dlaczego miałbyś kiedykolwiek chcieć
X& x = getx();
? Wystarczy użyćX x = getx();
i polegać na RVO.źródło
g(getx())
raczej zadzwonić niżg(getx().ref())
g
zamierza zmodyfikować coś, czego nie możesz już zdobyć.Obejście zła obejmuje słowo kluczowe „zmienne”. W rzeczywistości bycie złym jest dla czytelnika ćwiczeniem. Lub zobacz tutaj: http://www.ddj.com/cpp/184403758
źródło
Doskonałe pytanie, a oto moja próba bardziej zwięzłej odpowiedzi (ponieważ wiele użytecznych informacji znajduje się w komentarzach i trudno jest odszukać w hałasie).
Wszelkie odniesienia związane bezpośrednio z osobą tymczasową przedłuży jej życie [12.2.5]. Z drugiej strony odwołanie zainicjowane innym odwołaniem nie będzie (nawet jeśli ostatecznie będzie takie samo tymczasowe). Ma to sens (kompilator nie wie, do czego ostatecznie odnosi się to odniesienie).
Ale cały ten pomysł jest bardzo mylący. Np. Sprawi
const X &x = X();
, że tymczasowe będzie trwać tak długo, jakx
odniesienie, aleconst X &x = X().ref();
NIE będzie (kto wie, coref()
faktycznie powrócił). W tym drugim przypadkuX
wywoływany jest destruktor dla końca tej linii. (Można to zaobserwować za pomocą nietrywialnego destruktora).Wydaje się więc ogólnie mylące i niebezpieczne (po co komplikować reguły dotyczące czasu życia obiektów?), Ale przypuszczalnie istniała potrzeba przynajmniej stałych odwołań, więc standard określa dla nich takie zachowanie.
Wszystkie tymczasowe istnieją do końca pełnego wyrażenia. Jednak aby z nich skorzystać, potrzebujesz takiej sztuczki, jaką masz
ref()
. To legalne. Dodatkowy obręcz nie wydaje się być dobrym powodem, aby przypomnieć programiście, że dzieje się coś niezwykłego (mianowicie parametr odniesienia, którego modyfikacje zostaną szybko utracone).źródło
„Oczywiste jest, że obiekt tymczasowy nie jest stały w powyższej próbce, ponieważ dozwolone są wywołania funkcji niestałych. Na przykład ref () może modyfikować obiekt tymczasowy.”
W twoim przykładzie getX () nie zwraca stałej X, więc możesz wywołać ref () w taki sam sposób, jak możesz wywołać X (). Ref (). Zwracane jest odwołanie non const, więc można wywoływać metody non const, nie można tego przypisać do odwołania non const.
Wraz z komentarzem SadSidos powoduje to, że trzy punkty są nieprawidłowe.
źródło
Mam scenariusz, którym chciałbym się podzielić, gdzie chciałbym zrobić to, o co prosi Alexey. We wtyczce Maya C ++ muszę wykonać następujące shenanigan, aby uzyskać wartość atrybutu węzła:
Pisanie tego jest żmudne, dlatego napisałem następujące funkcje pomocnicze:
A teraz mogę napisać kod od początku postu jako:
Problem polega na tym, że nie kompiluje się poza Visual C ++, ponieważ próbuje powiązać zmienną tymczasową zwróconą z | operator do odwołania MPlug operatora <<. Chciałbym, aby był to odnośnik, ponieważ ten kod jest wywoływany wiele razy i wolałbym, aby MPlug nie był tak często kopiowany. Potrzebuję tylko tymczasowego obiektu, aby przeżyć do końca drugiej funkcji.
To mój scenariusz. Pomyślałem, że pokażę przykład, w którym chciałbym zrobić to, co opisuje Aleksiej. Z zadowoleniem przyjmuję wszystkie krytyki i sugestie!
Dzięki.
źródło