Какая стандартная формулировка говорит нам, что временное продление срока службы ref-to-const только "работает один раз"?
Мне был показан следующий пример в чате:
#include <iostream>
struct foo { ~foo() { std::cout << "destroying!\n"; } };
const foo& func(const foo& a, const foo&) { return a; }
int main()
{
foo x;
const foo& y = func(foo(), x);
std::cout << "main\n";
}
Выход:
destroying!
main
destroying!
Похоже, что время жизни временного файла foo
не распространяется на полноту main
, хотя оно привязано к ref-to-const
в этой области.
Предположительно, тогда расширение продолжительности жизни "работает только один раз"; то есть он применяется, когда аргументы func
инициализируются, но не передаются через последовательные привязки.
Является ли моя интерпретация правильной? Если это так (и если какой-либо отдельный параграф применим непосредственно), то какая стандартная формулировка определяет это поведение?
Ответы
Ответ 1
Это тема двух отчетов о проблемах, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1299 и http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1568.
Первый отчет о проблеме, который я являюсь репортером, должен был охватывать все эти случаи, когда ссылка привязана к временному объекту, но не предназначена для продления срока службы. Описание в теле проблемы только упоминает, что prvalues путают с временными выражениями (которые фактически решают, продлевается ли продолжительность жизни того, что они оценивают, или нет). Но lvalue и xvalues также смешиваются с ними в Стандарте. Пример, где это происходит в контексте static_cast
, - номер выпуска № 1568 (в котором использование "временной переменной" еще больше смущает вопрос).
Собственно, это:
Временная привязка к эталонному параметру в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов.
Противоречит другим правилам в том же параграфе. Поскольку временный обязан как опорного параметра в вызове функции и к локальной автоматической контрольной переменной.
Ответ 2
Ты почти прав. Такое поведение на самом деле происходит от вызова функции, а не из-за какого-либо правила "только работает один раз".
Здесь формулировка для всей функции расширения продолжительности жизни, с соответствующим правилом, выделенным жирным шрифтом:
[C++11: 12.2/5]:
[..] Временная привязка ссылки или временный объект, являющийся полным объектом подобъекта, к которому привязана ссылка, сохраняется для ресурса ссылки кроме
- [..]
- Временная привязка к ссылочному параметру в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов.
- [..]
Ответ 3
Применяемое здесь правило - здравый смысл. Стандарт
плохо сформулированные и на самом деле гарантируют это. Но нет
практический способ ее реализации.
Ответ 4
Возможно, я немного медленный, но для меня не стало ясно, что разрешение этого вопроса - от чтения других ответов. Таким образом, я изменил показанный код и хотел обобщить для других: ответ: вы получаете undefined поведение, если вы обращаетесь к y
!
Запустите этот код:
struct foo {
int id;
foo(int id) : id(id) { std::cout << "ctor " << id << std::endl; };
~foo() { std::cout << "dtor " << id << std::endl; }
};
const foo& func(const foo& a, const foo&) { return a; }
int main(int argc, char** argv) {
foo x(1);
const foo& y = func(foo(2), x);
std::cout << "main " << y.id << std::endl;
return 0;
}
Выход для меня:
ctor 1
ctor 2
dtor 2
main 2
dtor 1
Но строка main 2
- это undefined поведение.