Placement new для инициализации примитивного типа в самодельном union?
От: so5team https://stiffstream.com
Дата: 02.10.25 08:59
Оценка: 2 (1)
Доброго времени суток!

Нужна консультация от людей, которые знают формальную часть C++ лучше меня.

Делаю самодельный аналог union/variant. Что-то типа:

using void_ptr_t = void *;

template< typename T >
struct my_union_t
{
    alignas( std::max( alignof(T), alignof(void_ptr_t) ) )
    std::array< std::byte, std::max( sizeof(T), sizeof(void_ptr_t) ) > _content;

};

Т.е. это такое специализированное хранилище, в котором может быть либо экземпляр типа T, либо же указатель.
Либо вообще может ничего не быть, но это к делу не относится.

Важно то, что в качестве T может быть нетривиальный тип со своими конструкторами и деструктором. Вроде std::string или std::vector.

Когда мне нужно поместить в my_union_t::_content значение типа T, то естественным образом используется placement new:
template< typename T >
void
store_to( my_union_t<T> & dest, T value )
{
  new( dest._content.data() ) T{ std::move(value) };
}


Но вот нужен ли с формальной точки зрения placement new когда мне требуется туда сохранить значение указателя?
Т.е. должен ли я писать так:
template< typename T >
void
store_to( my_union_t<T> & dest, void_ptr_t value )
{
  new( dest._content.data() ) void_ptr_t{ value };
}

или же вполне достаточно и reinterpret_cast:
template< typename T >
void
store_to( my_union_t<T> & dest, void_ptr_t value )
{
  *(reinterpret_cast<void_ptr_t **>(dest._content.data())) = value;
}

?

Оно как бы понятно, что современные компиляторы без проблем возьмут и версию с reinterpret_cast. Но хочется понять, насколько это легально.

Сам думаю, что легальной является только версия с placement new и для T, и для void_ptr_t. Однако, не мешало бы получить какие-то подтверждения от знающих людей, чтобы быть уверенным.

PS. Вопрос о том, почему бы не взять std::variant выходит за рамки обсуждения, есть на то причины. Если брать обычный унаследованный из Си union, то для случаев, когда T является нетривиальным типом (вроде std::string) там свои заморочки, которые не делают код более понятным, скорее даже наоборот.
PPS. Про то, что для my_union_t нужны функции очистки содержимого и соответствующие процедуры для копирования/перемещения/swap я в курсе.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.