Временен живот в N3290 C++ чернова

Точка от черновата на N3290 C++, § 12.2, 5-та точка, ред 10.

Вторият контекст е, когато препратката е обвързана с временна. Временният елемент, към който е обвързана препратката, или временният обект, който е пълният обект на подобект, към който е обвързана препратката, продължава да съществува за целия живот на препратката, с изключение на:

Временно свързване към препратка в нов инициализатор (5.3.4) продължава до завършване на пълния израз, съдържащ новия инициализатор. [ Пример:

struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };
S* p = new S{ 1, {2,3} };// Creates dangling reference

— краен пример ] [ Забележка: Това може да въведе висяща препратка и реализациите се насърчават да издават предупреждение в такъв случай. — крайна бележка]

Това е добавената точка в сравнение с C++03. Но примерът не ми е разбираем. Можете ли да обясните тази точка с друг пример?

Знам какво представляват висящите препратки и временните обекти и че std::pair съдържа две стойности на вероятно различни типове данни.


person user751747    schedule 12.09.2011    source източник
comment
Обърнете внимание, че RValue-refs (което изглежда, за което говорите) се промени значително няколко пъти, тъй като стандартът беше модифициран. Ако не използвате FDIS (или още по-добре, действителния стандарт), така или иначе може да не виждате правилното обяснение.   -  person Billy ONeal    schedule 12.09.2011
comment
@Billy ONeal: Този текст остава, както е показано във FDIS, и въпреки че нямам достъп до финализирания стандарт, не трябва да има промяна от FDIS.   -  person David Rodríguez - dribeas    schedule 12.09.2011
comment
@David: Ако съвпада с FDIS, тогава трябва да е правилно, да. (В момента нямам достъп нито до FDIS, нито до окончателния стандарт)   -  person Billy ONeal    schedule 12.09.2011


Отговори (1)


Временните елементи като цяло продължават само до края на израза, в който са създадени:

#include <complex>


void func()
{
    std::complex<int>   a; // Real variable last until the end of scope.

    a = std::complex<int>(1,2) + std::complex<int>(3,4);
     // ^^^^^^^^^^^^^^^^^^^^^^  Creates a temporary object
     //                         This is destroyed at the end of the expression.
     // Also note the result of the addition creates a new temporary object
     // Then uses the assignment operator to change the variable 'a'
     // Both the above temporaries and the temporary returned by '+'
     // are destroyed at ';'

Ако създадете временен обект и го свържете към препратка. Вие удължавате живота му до същия живот на референцията, към която е обвързан.

    std::complex<int> const& b  = std::complex<int>(5,6);
                      //           ^^^^^^^^^^^^^^^^ Temporary object
                      // ^^^^                       Bound to a reference.
                      //                            Will live as long as b lives 
                      //                            (until end of scope)

Изключение от това правило е, когато временният елемент е обвързан с препратка в нов инициализатор.

    S* p1 = new S{ 1, {2,3} };
    // This is the new C++11 syntax that does the `equivalent off`:

    S* p2 = new S {1, std::pair<int,int>(2,3) };
                 //   ^^^^^^^^^^^^^^^^^^^^^^^    Temporary object.
                 //                              This lives until the end of the 
                 //                              expression that belongs to the new.
                 //                              ie the temporary will be destroyed
                 //                              when we get to the ';'

Но тук обвързваме новия временен обект към члена

const std::pair<int,int>& mp;

Това е постоянна препратка. Но временният обект, към който е свързан, ще бъде унищожен при ';' в горния израз, така че mp ще бъде препратка към обект, който вече не съществува, когато се опитате да го използвате в следващите изрази.

}
person Martin York    schedule 12.09.2011
comment
Така че разговорно можете да кажете, че препратките към членовете на класа не могат да удължат живота на временните елементи, преминали през конструктора. Това е добра точка за вкъщи. Добър отговор, между другото! - person Kerrek SB; 12.09.2011
comment
@Kerrek SB: Интересното е, че цитатът изглежда показва обратното, не съвсем, но някак. Фактът, че споменава само new-initializerаргумент конструктор в предишен ред, който е изрязан от въпроса), заедно с липсата на коментар във втория ред от примерния код изглежда показва, че намерението позволява този втори ред да не създава висяща препратка. т.е. нищо в стандарта не казва, че по време на агрегатната инициализация животът не се удължава. От друга страна, както g++4.5, така и clang++2.8 създават висяща препратка за този втори ред - person David Rodríguez - dribeas; 12.09.2011
comment
@Дейвид: А, да. Така че трябва да бъде динамично разпределените обекти не могат да удължат живота на временните елементи, така ли е? Това всъщност има идеален смисъл. - person Kerrek SB; 12.09.2011
comment
@Kerrek SB: :) Все пак малко по-сложно от това, неагрегатите изискват използването на конструктор и това е обхванато в точката, която отбелязах по-горе, и дори не съм сигурен за намерението на тази конкретна промяна, но най-доброто, което мога да измисля, е: само агрегати без динамично хранилище може да могат да удължат живота на временното чрез обвързване на постоянна препратка може което показва, че не съм виждал никъде в стандарта, където това е забранено, дори ако компилаторите, до които имам достъп, не го правят. - person David Rodríguez - dribeas; 12.09.2011
comment
Дейвид: Хммм. Ако са изключени само new изрази, тогава всеки друг израз, включително извиквания на конструктор за автоматично разпределение, трябва да бъде включен в ситуациите, които удължават живота на временния елемент. Просто стандартът изобщо не разглежда ли това? Ще направя няколко теста с GCC, просто от любопитство. - person Kerrek SB; 12.09.2011
comment
Всъщност може да се окаже, че извикванията на конструктора при директна инициализация не представляват обвързване на временна към const-референция и по-скоро временната съществува само за продължителността на извикването на конструктора. Тоест, фактът, че самото извикване на функцията обвързва своя аргумент с друга константна препратка, не е от значение за оригиналния временен. - person Kerrek SB; 12.09.2011
comment
@Kerrek: Мисля, че в тази ситуация обвързваме временното с параметъра, използван в конструктора (генериран от компилатора). Така че животът се удължава до живота на параметъра (за съжаление това не е по-дълго от временно, така че все още се унищожава в края на обхвата). Именно това скрито обвързване се опитва да изясни коментарът в стандарта, така че хората да не мислят, че е обвързано директно с члена и по този начин е удължено до живота на обекта. - person Martin York; 12.09.2011