Связана ли константная ссылка с другой ссылкой, которая была преобразована из временной висячей ссылки?

Ниже приведен фрагмент кода:

#include <iostream>
using namespace std;
struct B{
     int b;
     ~B(){cout <<"destruct B" << endl;}
};
B func(){
    B b;
    b.b = 1;
    return b;
}
int main(){
    const B& instance = (const B&)func(); //is `instance` a dangling reference?
    cout <<instance.b<<endl;
    return 0;
}

в этом онлайн-компиляторе вывод

destruct B
destruct B
1

Таким образом, возвращаемое значение, похоже, уничтожается раньше, чем операция cout. Таким образом, instance кажется оборванной ссылкой.

Если мы изменим const B& instance = (const B&)func(); на const B& instance =func();, то результат будет

destruct B
1
destruct B

В качестве дополнения, если тестирую код в vs2015, то вывод последний. Однако при тестировании в gcc (до 4.6) вывод будет первым, но вторым в версии после 4.6. Поэтому я хочу знать, ошибается ли онлайн-компилятор или ссылка на самом деле болтается.


person scottxiao    schedule 26.05.2018    source источник
comment
Вы сами ответили на свой вопрос прямо в заголовке: ваша ссылка не привязана к временной.   -  person Kerrek SB    schedule 26.05.2018
comment
@KerrekSB Я не могу понять твою точку зрения. Я имею в виду, что (const B&)func() — это временная ссылка, связанная с временным возвращаемым объектом. Таким образом, instance привязан к этой временной ссылке, которая является временной.   -  person scottxiao    schedule 26.05.2018
comment
Из [class.temporary]/6: const int& x = (const int&)1; // temporary for value 1 has same lifetime as x   -  person cpplearner    schedule 26.05.2018
comment
@cpplearner Значит, онлайн-компилятор делает что-то не так?   -  person scottxiao    schedule 26.05.2018
comment
@KerrekSB Я отредактировал заголовок, чтобы сделать его более понятным.   -  person scottxiao    schedule 26.05.2018
comment
@KillzoneKid Вы имеете в виду, что у компилятора могут быть проблемы?   -  person scottxiao    schedule 26.05.2018
comment
@bigxiao: Хм, я ошибся там, что касается C ++ 17, где изменилась формулировка продления срока службы. В С++ 11 я считаю, что выражение (const B&)func() не является временным (потому что это lvalue, а не значение prvalue), а в C++ 11 только временные выражения получают продление срока службы. С++ 17 позволяет продлевать время жизни glvalues ​​(для поддержки материализации, я подозреваю), и это, похоже, имело побочный эффект, позволяя вашему приведению, которое теперь приводит glvalue к glvalue (поскольку prvalue уже материализовалось в этот момент ).   -  person Kerrek SB    schedule 26.05.2018
comment
Ах, похоже, что язык был задним числом изменен через отчет о дефекте wg21.link/cwg1299, так что теперь C++11 тоже работает по-новому. (В DR говорится, что раньше временными считались только значения prvalue, а новые правила более либеральны.)   -  person Kerrek SB    schedule 26.05.2018
comment
@KerrekSB Так что ссылка висит до cpp11?   -  person scottxiao    schedule 26.05.2018
comment
@bigxiao: Раньше это была висячая ссылка и в C++ 11, до июля 2017 года, когда она стала действительной в C++ 11. То, как отчеты о дефектах применяются к более старым версиям стандарта, очень туманно, поскольку официально они применяют только один и только текущий стандарт. Каждая публикация заменяет предыдущую. Формулировка DR не может буквально применяться к C++03, в котором не было glvalues, но ваш поставщик может также выбрать продление срока службы в режиме C++03 теперь на основе этого DR. Думаю, это было бы разумной интерпретацией.   -  person Kerrek SB    schedule 26.05.2018
comment
Похоже, поведение зависит от того, какой стандарт С++ вы используете. Я рекомендую использовать С++ 17, но если это слишком сложно, тогда С++ 14.   -  person Eljay    schedule 26.05.2018
comment
Вы также можете добавить диагностику в конструкторы по умолчанию и конструкторы копирования, а также включить this в свою диагностику. Это должно оказаться очень информативным.   -  person Sam Varshavchik    schedule 26.05.2018
comment
@SamVarshavchik Я не могу понять вашу точку зрения, не могли бы вы привести пример кода?   -  person scottxiao    schedule 26.05.2018
comment
Вы печатаете сообщение в своем деструкторе. Просто напечатайте другое сообщение по умолчанию и скопируйте конструкторы. Включение this в ваше сообщение позволит вам точно увидеть, какие конкретные экземпляры вашего класса создаются и уничтожаются и когда.   -  person Sam Varshavchik    schedule 26.05.2018
comment
Вот пример: на старом GCC: wandbox.org/permlink/N36VET6Vo2ZvcGpF. Затем переключитесь на более новый GCC, чтобы увидеть изменения в поведении. Обратите внимание, что вопрос о CWG обсуждается уже давно, вот только официально он был принят в прошлом году. Но производители, похоже, уже давно это внедрили.   -  person Kerrek SB    schedule 26.05.2018
comment
@KerrekSB Почему он болтается в более ранней версии cpp11, то есть где cpp17 меняет более раннюю версию cpp11?   -  person scottxiao    schedule 26.05.2018
comment
@bigxiao: Из-за того, как работают отчеты о дефектах. Обратите внимание, что, строго говоря, это также все еще висит в более ранних версиях C++ 17 - опубликованный C++ 17 содержит старое поведение; отчет о дефекте был принят только после завершения C++17 (например, см. wg21.link/N4659). Но стандарт означает опубликованный документ вместе со всеми впоследствии принятыми отчетами о дефектах.   -  person Kerrek SB    schedule 26.05.2018
comment
Является ли выражение временным?   -  person curiousguy    schedule 29.05.2018


Ответы (1)


Согласно последнему проекту [class.temporary]/6 ( нерелевантная часть мной опущена):

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

  • ...

  • преобразование const_cast ([expr.const.cast]), static_cast ([expr.static.cast]), dynamic_cast ([expr.dynamic.cast]) или reinterpret_cast ([expr.reinterpret.cast]) без участия пользователя -определенное преобразование, операнд значения gl, который является одним из этих выражений, в значение gl, которое ссылается на объект, обозначенный операндом, или на его полный объект или его подобъект,

  • ...

... [ Примечание. Явное преобразование типа ([expr.type.conv], [expr.cast]) интерпретируется как последовательность элементарных приведений, описанных выше. [ Пример:

const int& x = (const int&)1;  // temporary for value 1 has same lifetime as x

— конец примера ] — конец примечания ]

Ваш код правильно сформирован.


До C++14 формулировка в стандарте неясна в отношении такого случая, и существует проблема дефекта 1376. Эта проблема поясняет, что в таком случае время жизни временного объекта не должно быть продлено. Однако это разъяснение заменено вопросом 1299 (чье разрешение не включено даже в C++17, а в текущий черновик).

Таким образом, вы можете сделать вывод, что до решения проблемы 1299 это ошибка для GCC с версией после 4.6. Существует также отчет об ошибке 52202 для GCC.

person xskxzr    schedule 26.05.2018
comment
А как насчет auto&& ref=std::forward(T{});? ref болтается? - person scottxiao; 26.05.2018
comment
@bigxiao Не продлен. Потому что ни одно из правил в [class.temporary]/6 не применяется. - person xskxzr; 26.05.2018
comment
@bigxiao Правило продления срока службы временного объекта - это набор очень особых случаев, если вы выйдете за рамки этих узко определенных случаев, вы не застрахованы. (Это слишком сложно, если вы спросите меня.) - person curiousguy; 09.06.2018