С++ - использование константной ссылки для продления члена временного, нормально или UB?
рассмотрим что-то вроде этого:
#include <iostream>
struct C {
C(double x=0, double y=0): x(x) , y(y) {
std::cout << "C ctor " << x << " " <<y << " " << "\n";
}
double x, y;
};
struct B {
B(double x=0, double y=0): x(x), y(y) {}
double x, y;
};
struct A {
B b[12];
A() {
b[2] = B(2.5, 14);
b[4] = B(56.32,11.99);
}
};
int main() {
const B& b = A().b[4];
C c(b.x, b.y);
}
когда я компилирую с -O0, я получаю печать
C ctor 56.32 11.99
но когда я компилирую с -O2, я получаю
C ctor 0 0
Я знаю, что мы можем использовать константную ссылку для продления локального временного, так что-то вроде
const A& a = A();
const B& b = a.b;
было бы совершенно законно. но я изо всех сил пытаюсь найти причину, почему один и тот же механизм/правило не применяется для каких-либо временных
ОБНОВЛЕНИЕ ДЛЯ БУДУЩЕЙ ССЫЛКИ:
Я использую GCC версии 6.3.0
Ответы
Ответ 1
Ваш код должен быть правильно сформирован, потому что для временных
(выделение мое)
Всякий раз, когда ссылка связана с временным объектом или с его подобъектом, срок действия временного элемента увеличивается, чтобы соответствовать времени жизни ссылки
.Учитывая A().b[4]
, b[4]
является подобъектом b
, а элемент данных b
является подобъектом временного массива A()
, время жизни которого должно быть увеличено.
LIVE на Clang10 с -O2
LIVE на gcc10 с -O2
Кстати, это похоже на gcc ошибку, которая была исправлена.
Из стандарта [класс.время]/6
Третий контекст - это когда ссылка связана с временным объектом. 36 Временный объект, к которому привязана ссылка, или временный объект, являющийся полным объектом подобъекта, к которому привязана ссылка, сохраняется в течение всего времени существования объекта. ссылка, если glvalue, к которому привязана ссылка, была получена одним из следующих способов:
...
[ Example:
template<typename T> using id = T;
int i = 1;
int&& a = id<int[3]>{1, 2, 3}[i]; // temporary array has same lifetime as a
const int& b = static_cast<const int&>(0); // temporary int has same lifetime as b
int&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0);
// exactly one of the two temporaries is lifetime-extended
— end example ]
Ответ 2
A(). B [4] не является temp или rvalue, поэтому не работает. Хотя A() является временным, вы создаете ссылку на элемент массива, который существует в точке создания. Затем dtor запускает A(), что означает, что последующий доступ к b.b становится несколько неопределенным поведением. Вы должны держаться за A & чтобы убедиться, что б остается в силе.
const A& a = A();
const B& b = a.b[4];
C c(b.x, b.y);