Пожалуйста, рассмотрите этот бит кода:
#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 немедленно, перед всем, затем перезаписывает его при вызове методов и назначений, другой делает это в самом конце, после всего.
В. Что было бы объяснение такого поведения?
Спасибо.
Да, это неопределенное поведение:
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
бессодержательным. Что кажется концептуально странным для меня, но формулировка в этом отношении ясна.
Других решений пока нет …