Определено ли поведение для использования нового размещения для изменения типа переменной, которая хранится в reference_wrapper?

Я возился с новым размещением и сделал этот код:

#include <iostream>
#include <functional>
#include <new>

int main()
{
    char memory[sizeof(int)]; //memory to hold type
    auto ref = std::ref(*new (memory) int{5}); //allocating int in memory, and make a reference to it
    std::cout << ref << std::endl;
    new (memory) unsigned{6}; //"overwrite"
    std::cout << ref << std::endl;
}

Вывод 5, затем 6, но правильно ли он определен? Если да, можно ли вместо этого использовать float в качестве второго типа?


person xinaiz    schedule 24.02.2017    source источник
comment
Что касается жизни, вы разыменовываете висячий указатель. Однако прямо сейчас я не могу просмотреть стандарт для этого, так что есть шанс, что там будет угловой случай.   -  person chris    schedule 24.02.2017
comment
Вы обращаетесь к unsigned int через std::reference_wrapper<int>, который является оболочкой ссылки на int. Поэтому мы можем сказать, что вы получаете доступ к unsigned int через int &. Это законно? Я бы сказал УБ.   -  person skypjack    schedule 25.02.2017
comment
Кроме того, в стандарте говорится: Ссылка должна быть инициализирована для ссылки на допустимый объект или функцию. Следовательно, ваша попытка изменить лежащий в основе объект определенно пахнет.   -  person skypjack    schedule 25.02.2017
comment
Реализация здесь: en.cppreference.com/w/cpp/utility/functional/ reference_wrapper, говорит, что reference_wrapper на самом деле реализован с использованием указателя, поэтому здесь нет ссылки. Возможно, при доступе к нему с помощью operator T& ().   -  person marcinj    schedule 25.02.2017
comment
@marcinj Я получил определение из здесь. При этом то же самое относится и к указателям, поэтому подробности реализации здесь кажутся бессмысленными. Доступ к объекту U через T * редко бывает хорошей идеей (не считая полиморфизма, конечно).   -  person skypjack    schedule 25.02.2017
comment
в случае указателей это может нарушить строгое правило псевдонимов. Но gcc позволяет unsigned int использовать псевдоним int (gcc.gnu.org/onlinedocs/gcc /Optimize-Options.html). Но это то, что делает gcc, а не то, что говорит стандарт (по крайней мере, насколько мне известно).   -  person marcinj    schedule 25.02.2017
comment
Какие бы другие правила это ни нарушало (и почти наверняка нарушает одно из правил времени жизни объекта), оно не нарушает строгое правило псевдонимов.   -  person T.C.    schedule 25.02.2017
comment
Вы также должны рассмотреть вопросы выравнивания. С простым буфером char у вас нет гарантии, что память правильно выровнена. Уже с int вы должны использовать std::aligned_storage. С несколькими типами (int, float...) взгляните на std::aligned_union   -  person manlio    schedule 27.02.2017


Ответы (1)


Второе размещение new-expression повторно использует хранилище и, следовательно, завершает время жизни объекта int, созданного первым размещением new-expression ([basic.life]/1.4).

Поскольку [basic.life]/8 не выполняется (из-за к различию типов), указатель, сохраненный оболочкой ссылки, не указывает на новый объект, а продолжает указывать на объект int вне срока службы. Таким образом, доступ к объекту через lvalue, полученный из такого указателя, имеет неопределенное поведение согласно [basic .life]/7.


Обратите внимание, что строгие псевдонимы здесь неуместны; это правило явно разрешает доступ к объекту через значение gl "типа, который является подписанным или беззнаковый тип, соответствующий динамическому типу объекта".

person T.C.    schedule 25.02.2017