"T const и t = C(). A;" продлить срок службы "а"?
Дается следующая сценария, которая должна интерпретироваться как код С++ 0x:
struct B { };
struct A { B b; };
int main() {
B const& b = A().b;
/* is the object still alive here? */
}
Clang и GCC (версия для стволов по состоянию на 2011/02 год) ведут себя по-другому: Clang продлевает срок службы. GCC перемещает B
в новый временный объект, а затем привязывает ссылку к этому новому временному.
Я не могу найти, что любое поведение может быть получено из слов Стандарта. Выражение A().b
не является временным (см. 5.2.5). Может кто-нибудь объяснить мне следующее?
- Желаемое поведение (цель комитета)
- Поведение, которое вы выведете из FDIS
Спасибо!
Ответы
Ответ 1
В пункте 12.2 параграфа N3126 = 10-0116 говорится, что:
Второй контекст [в котором временные разрушен в другой точке, чем конец полное выражение] - это когда ссылка привязан к временному. Временный к которой ссылка привязана, или временный, являющийся полным объектом подобъекта, к которому ссылка связана с сохранением времени жизни ссылка кроме...
а затем следует список из четырех специальных случаев (ctor-инициализаторы, ссылочные параметры, возвращаемое значение, новый инициализатор).
Итак (в этой версии) мне кажется, что clang является правильным, потому что вы привязываете ссылку на подобъект временного.
ИЗМЕНИТЬ
Думая на базовый под-объект объекта, это также кажется единственным разумным поведением. Альтернативой будет означать, что вырезать в:
Derived foo();
...
void bar()
{
Base& x = foo(); // not very different from foo().b;
...
}
На самом деле, после небольшого эксперимента, действительно, что g++ отличает субобъект-член и базовый под-объект, но я не понимаю, где это дифференцирование выполняется в стандарте. Ниже приведена тестовая программа, которую я использовал, и там, где она отчетливо видна при обращении с двумя случаями... (B
is Base, D
Производится и C
).
#include <iostream>
struct B
{
B()
{ std::cout << "B{" << this << "}::B()\n"; }
B(const B& x)
{ std::cout << "B{" << this << "}::B(const B& " << &x << ")\n"; }
virtual ~B()
{ std::cout << "B{" << this << "}::~B()\n"; }
virtual void doit() const
{ std::cout << "B{" << this << "}::doit()\n"; }
};
struct D : B
{
D()
{ std::cout << "D{" << this << "}::D()\n"; }
D(const D& x)
{ std::cout << "D{" << this << "}::D(const D& " << &x << ")\n"; }
virtual ~D()
{ std::cout << "D{" << this << "}::~D()\n"; }
virtual void doit() const
{ std::cout << "D{" << this << "}::doit()\n"; }
};
struct C
{
B b;
C()
{ std::cout << "C{" << this << "}::C()\n"; }
C(const C& x)
{ std::cout << "C{" << this << "}::C(const C& " << &x << ")\n"; }
~C()
{ std::cout << "C{" << this << "}::~C()\n"; }
};
D foo()
{
return D();
}
void bar()
{
std::cout << "Before calling foo()\n";
const B& b = foo();
std::cout << "After calling foo()\n";
b.doit();
std::cout << "After calling b.doit()\n";
const B& b2 = C().b;
std::cout << "After binding to .b\n";
b2.doit();
std::cout << "After calling b2.doit()\n";
}
int main()
{
std::cout << "Before calling bar()\n";
bar();
std::cout << "After calling bar()\n";
return 0;
}
Выход, который я получаю с g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5,
Before calling bar()
Before calling foo()
B{0xbf9f86ec}::B()
D{0xbf9f86ec}::D()
After calling foo()
D{0xbf9f86ec}::doit()
After calling b.doit()
B{0xbf9f86e8}::B()
C{0xbf9f86e8}::C()
B{0xbf9f86e4}::B(const B& 0xbf9f86e8)
C{0xbf9f86e8}::~C()
B{0xbf9f86e8}::~B()
After binding to .b
B{0xbf9f86e4}::doit()
After calling b2.doit()
B{0xbf9f86e4}::~B()
D{0xbf9f86ec}::~D()
B{0xbf9f86ec}::~B()
After calling bar()
По-моему, это либо ошибка в g++, либо ошибка в том, что соответствует стандарту С++, если это действительно ожидаемое поведение или возможное приемлемое поведение (но я должен сказать, что я не очень много думал об этом, это просто ощущение, что с этой дифференциацией что-то не так).
Ответ 2
Хорошо, я делаю 180 градусов на этом
После обновления моих знаний о стандарте, я должен признать
что, вероятно, справедливо ожидать, что объект, на который ссылается b
, останется в живых (будет расширен) для продолжительности области действия, в которой const & был инициализирован. Я нашел GotW # 88 полезным источником для этого.
Я не вижу, как A().b
структурно или семантически отличается от
string f() { return "abc"; } // ABC initializes return-value **TEMP**
void g() {
const string& s = f(); // initializes with reference to a temp
cout << s << endl; // '*&s' is extended per standard
}
Извините за любую путаницу, которую я мог вызвать. Я был немного из глубины.
Ответ 3
Временные объекты отличаются обстоятельствами их создания. (§12.2 "Временные классы типа класса создаются в разных контекстах..." )
Для временных, созданных ссылочным декларатором, §12.2 относится к §8.5. С++ 03 и С++ 11 сильно отличаются в разделе 8.5.5, но оба явно поддерживают ваш код.
С++ 03 говорит, что либо
- Ссылка привязана к объекту, представленному rvalue (см. 3.10), или к под-объекту внутри этого объекта.
- Создается временный тип "cv1 T2" [sic], и вызывается конструктор для копирования всего объекта rvalue во временный. Ссылка привязана к временному или к под-объекту в пределах временного.
Обсуждение полностью относится к подобъектам, не отличая базовые классы от членов. Таким образом, если привязка ссылки к элементу запрещена, то так привязывает элемент к базе, что исключает ScopeGuard.
С++ 11 более подробный, но указывает
- В противном случае ссылка должна быть ссылкой lvalue на нелетучий const-тип (то есть cv1 должен быть const), или ссылка должна быть ссылкой rvalue.... Если выражение инициализатора... представляет собой значение xvalue, класс prvalue, значение prvalue массива или значение lvalue функции и "cv1 T1" совместимый с "cv2 T2"... тогда ссылка привязана к значению выражения инициализатора. "
В сочетании с ответом 6502 и бессмысленностью привязки ссылки к значению, которое заканчивается в точку с запятой, очевидно, что С++ 11 продолжает поддерживать это поведение.
Ответ 4
Посмотрим (все ссылки на FDIS):
struct B { };
struct A { B b; };
int main() {
B const& b = A().b;
}
1) 5.2.3/2 говорит, что A()
является prvalue.
2) 5.2.5/4 говорит, что A().b
является prvalue из-за точки 1).
3) 8.5.3/5 говорит, что B const& b
привязывается непосредственно к A().b
без создания временного.
4) 12.2/5 говорит, что время жизни временной привязки к ссылке расширено.
Итак, похоже, что GCC здесь не так.
Правильно ли Clang или если это UB зависит от того, является ли подобъект временного временным. Я вполне уверен, что ответ должен быть положительным, но Стандарт, похоже, молчал об этом. Если кто-то представляет DR?
EDIT: Как указано в @6502, 3.7.5 указывает, что время жизни подобъекта - это время жизни его полного объекта.