В двух операторах печати печатаются разные числа. Насколько я понимаю, я не делаю здесь никаких хитрых const_cast, поэтому я не уверен, что я мог совершить.
Правильно ли сформирован этот код?
Может ли компилятор полагаться на тот факт, что A::num - это const, поэтому ему разрешено печатать одно и то же число?
Код:
struct A
{
const int num = 100;
A() {}
A(int in) : num{in} {}
void call()
{
new (this) A{69};
}
};
int main()
{
A a;
std::cout << a.num << '\n';
a.call();
std::cout << a.num << '\n';
}
Я собираюсь рискнуть и сказать, что это почти наверняка неопределенное поведение placement new в памяти, которая уже содержит существующий экземпляр класса. Вам, вероятно, это сойдет с рук (хотя я предполагаю, что компилятор вас предупредит), потому что не задействованы выделения памяти или виртуальные функции, но это кажется самой изворотливой из хитрых идей.
Я подозреваю, что любое неопределенное поведение с большей вероятностью связано с constness num, чем с тем, что память уже содержит существующий экземпляр класса, поскольку деструктор этого класса в любом случае ничего не сделает.
@ShadowRanger Вы не поверите, но вам разрешено повторно использовать хранилище живых объектов! [basic.life] "Программа может закончить время жизни объекта, повторно используя память, которую занимает объект, или явно вызвав деструктор для объекта с нетривиальным деструктором. Для объекта типа класса с нетривиальным деструктором, программе не требуется явно вызывать деструктор перед повторным использованием или освобождением памяти, занимаемой объектом ... "
@RaymondChen Это правда, даже если константен только член? «полный объект» определяется в [intro.object] как «объект, который не является подобъектом какого-либо другого объекта, называется законченным объектом».
@RaymondChen a, а не const, поэтому новое размещение не UB.
@ShadowRanger "определенно неопределенное поведение для размещения нового в памяти, которая уже содержит существующий экземпляр класса" нет, в C++ совершенно законно делать что-либо с памятью существующих объектов (кроме некоторых глобальных константных объектов); но это уничтожает эти ранее существовавшие объекты, они больше не существуют как объекты. Виртуальные функции "потому что не задействованы выделения памяти или виртуальные функции" здесь не имеют значения, но попытка использовать объект, который не существует (потому что вы его стерли), делает
«Я не делаю изворотливого const_cast». На любом языке, допускающем низкоуровневое программирование, с прямым использованием указателя на память и ручным управлением памятью, у вас будет возможное неопределенное поведение без приведения указателя или другой «хитрой» конструкции. Это прямое следствие доверия программисту к низкоуровневой памяти и управлению сроком службы. Без него C++ не был бы C++.





Нет, в вашем коде есть UB. Удалите const на num, и вы больше не получите UB.
Проблема в том, что стандарт гарантирует, что объект const не изменится. Но если вы повторно используете одно и то же хранилище, вы можете каким-то образом «модифицировать» объект const.
[basic.life] p8 явно запрещает это, говоря, что старое имя объекта относится к новому объекту только при определенных условиях. Один из них заключается в том, что в вашем классе нет константных членов. Итак, по расширению ваш второй a.num - это UB, поскольку a относится к старому разрушенному объекту.
Однако есть два способа избежать этого UB. Во-первых, вы можете сохранить указатель на новый объект:
struct A *new_ptr;
struct A {
// [...]
void call() {
new_ptr = new (this) A{69};
}
};
int main()
{
A a;
std::cout << a.num << '\n';
a.call();
std::cout << new_ptr->num << '\n'; // ok
}
Или используйте std::launder:
std::cout << std::launder(&a)->num << '\n'; // second access
да. Но обратите внимание, что согласно теории (постулируемой стандартом std) указатели являются POD (или тривиальными типами), использование std::launder по своей сути избыточно, поскольку все тривиальные типы данного типа и данное побайтовое представление должны иметь одно и то же намеренное значение, а не одно и то же числовое значение. . Или банальный шрифт - действительно бесполезное понятие.
Каков результат, и чего вы ожидаете от него?