Это неопределенное поведение или ошибка в структуре init?

Пожалуйста, рассмотрите этот бит кода:

#include <iostream>

int main()
{
struct A
{
int x;
int y;
int z;

int foo()
{
std::cout << "enter foo: " << this->x << "," << this->y << "," << this->z << std::endl;
return 5;
}

int moo()
{
std::cout << "enter moo: " << this->x << "," << this->y << "," << this->z << std::endl;
this->x = 1;
this->z = 10;
return 2;
}
};

A b { b.foo(), b.z = b.moo(), 3};

std::cout << "final: " << b.x << "," << b.y << "," << b.z << std::endl;

return 0;
}

Результат в моем VS2017 (выпуск x64):

enter foo: 0,0,0
enter moo: 5,0,0
final: 1,2,3

Результат от ideone.com (gcc 6.3) https://ideone.com/OGqvjW):

enter foo: 0,0,3
enter moo: 5,0,3
final: 1,2,2

Один компилятор устанавливает z член 3 немедленно, перед всем, затем перезаписывает его при вызове методов и назначений, другой делает это в самом конце, после всего.

В. Что было бы объяснение такого поведения?

Спасибо.

4

Решение

Да, это неопределенное поведение:

int foo()
{
std::cout << "enter foo: " << this->x << "," << this->y << "," << this->z << std::endl;
//                            ~~~~~~~           ~~~~~~~           ~~~~~~~
}

В тот момент, когда foo() вызывается, x, y, а также z еще не были инициализированы. От [Dcl.init] / 12:

Если для объекта не указан инициализатор, объект инициализируется по умолчанию. Когда хранилище для объекта с автоматической или динамической продолжительностью хранения получено, объект имеет неопределенное значение, и если для объекта не выполняется инициализация, этот объект сохраняет неопределенное значение до тех пор, пока это значение не будет заменено ([expr.ass]). […] Если в результате оценки получено неопределенное значение, поведение не определено, за исключением следующих случаев: […]

Ни один из оставшихся случаев не применяется. Итак, печать x, y, а также z есть неопределенное поведение. Ничем не отличается от просто:

int x;
std::cout << x; // ub

Мой предыдущий ответ сказал да, но по причинам жизни. Он предположил, что инициализация в A b{ b.foo(), b.z = b.moo(), 3}; не пустует и, следовательно, любой доступ любого из членов b до окончания инициализации UB. Тем не менее, xskxzr с тех пор указал мне, что для того, чтобы инициализация была не пустым, вы должны быть вызваны конструкторы, а также int не имеет конструкторов. Это делает инициализацию b бессодержательным. Что кажется концептуально странным для меня, но формулировка в этом отношении ясна.

7

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector