С++ - использование константной ссылки для продления члена временного, хорошо или 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 для продления локального временного файла, поэтому что-то вроде

const A& a = A();
const B& b = a.b;

было бы совершенно законно. но я изо всех сил пытаюсь найти причину, по которой тот же механизм/правило не применяется ни к какому временному

ИЗМЕНИТЬ ДЛЯ БУДУЩЕГО ИСПОЛЬЗОВАНИЯ:

Я использую gcc версии 6.3.0


person user2717954    schedule 10.09.2019    source источник
comment
Я не знаю, какой компилятор/инструментарий вы используете. Я протестировал это с помощью C++2a + последний CLang (HEAD) и, кажется, работает нормально -› wandbox.org /permlink/CNRZzNSXlD4NQUNg и, как вы можете видеть, введена следующая команда: clang++ prog.cc -Wall -Wextra -O2 -march=native -I/opt/wandbox/boost-1.71.0/clang-head/include -std=gnu++2a -pedantic   -  person mutantkeyboard    schedule 10.09.2019
comment
gcc 6.3.0 (доступная версия в моем офисе)   -  person user2717954    schedule 10.09.2019
comment
Компиляция @mutantkeyboard без ошибок означает только то, что она синтаксически корректна. Это не значит, что он действителен. И запуск без ошибок не означает, что она действительна, UB означает, что она может работать без каких-либо сообщений об ошибках, дающих ожидаемый результат, но это все еще UB, и поэтому программа не будет валидной.   -  person t.niese    schedule 10.09.2019
comment
@t.niese Полностью с вами согласен. Дело было не в этом. Меня больше интересовало, как в этой ситуации ведет себя другой компилятор/тулчейн, так как я нашел это довольно интересным поведением. Вот почему я попросил его дать мне версию GCC/CLANG :) Я немного изучаю внутренности компилятора, так что это было интересно протестировать.   -  person mutantkeyboard    schedule 10.09.2019
comment
@mutantkeyboard, но тогда [...]and seems to work fine[...] действительно вводит в заблуждение, поскольку подразумевает, что вы считаете его действительным только потому, что он компилируется, и вы не получаете никакого сообщения об ошибке. На вопрос нельзя ответить путем тестирования, если он компилируется без каких-либо сообщений об ошибках. (за исключением случаев, когда вы знаете компилятор с настройками компилятора, которые точно не хотели бы говорить о возможном UB из-за const ref).   -  person t.niese    schedule 10.09.2019


Ответы (2)


Ваш код должен быть правильно сформирован, потому что для временных файлов

(выделено мной)

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

Учитывая A().b[4], b[4] является подобъектом b, а член данных b является подобъектом temproray A(), время жизни которого должно быть увеличено.

ПРЯМОЙ ЭФИР на clang10 с -O2
ЖИВАТЬ на gcc10 с -O2

Кстати: похоже, это ошибка gcc, которая была исправлена.

Из стандарта [class.temporary]/6

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

...

[ Пример:

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

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

person songyuanyao    schedule 10.09.2019
comment
Для справки: соответствующий стандартный раздел — eel.is/c++draft/class. временный#6 - person Max Langhof; 10.09.2019
comment
так это ошибка в моей версии gcc? - person user2717954; 10.09.2019
comment
В качестве примечания: работает начиная с gcc 8.2 и clang 4.0, до этих версий не работает в gcc и clang. - person t.niese; 10.09.2019
comment
@t.niese Да, похоже, это ошибка gcc, которая был исправлен. - person songyuanyao; 10.09.2019
comment
@user2717954 Кажется, я нашел это. - person songyuanyao; 10.09.2019

A().b[4] не является временным или rvalue, поэтому он не работает. Хотя A() является временным, вы создаете ссылку на элемент массива, который существует на момент создания. Затем dtor срабатывает для A(), что означает, что более поздний доступ к b.b становится несколько неопределенным поведением. Вам нужно будет удерживать A&, чтобы убедиться, что b остается в силе.

    const A& a = A();
    const B& b = a.b[4];
    C c(b.x, b.y);
person robthebloke    schedule 10.09.2019
comment
По крайней мере, начиная с gcc 8.2 и clang 4.0 время жизни A и B продлевается при записи только const B& b = A().b[4]; - person t.niese; 10.09.2019
comment
если другой ответ (и цитаты из стандарта, который он делает) верен, то этот ответ неверен - person 463035818_is_not_a_number; 10.09.2019