выровненное хранилище и строгое наложение

В настоящее время я использую align_storage для реализации типа ‘Optional’, аналогичного типу boost :: option. Для этого у меня есть такой ученик:

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;

Я использую размещение new, чтобы создать объект, однако я не храню указатель, возвращенный где-либо. Вместо этого я обращаюсь к базовому типу объекта во всех моих функциях-членах, как это (очевидно, с помощью проверок, чтобы убедиться, что объект действителен через логический флаг, также хранящийся в моем дополнительном типе):

T const* operator->() const {
return static_cast<T const*>(static_cast<void const*>(&t_));
}

Мой вопрос: безопасно ли это? Насколько я понимаю, мое использование размещения new меняет «динамический тип» объекта, и до тех пор, пока я продолжаю обращаться к памяти с помощью этого типа, я буду в порядке. Однако мне не ясно, должен ли я держать указатель, возвращаемый из места размещения, новым или мне разрешено просто приводить к базовому типу всякий раз, когда мне нужно получить к нему доступ. Я прочитал раздел 3.10 стандарта C ++ 11, однако я не достаточно свободно владею стандартами, чтобы быть уверенным.

Если возможно, я бы чувствовал себя лучше, если бы вы могли дать ссылку на стандарт в своем ответе (это помогает мне спать по ночам: P).

28

Решение

ABICT ваше использование безопасно.

  • Размещение нового объекта типа T создаст объект, начиная с переданного адреса.

§5.3.4 / 10 говорит:

Выражение new передает количество запрошенного пространства
функция выделения в качестве первого аргумента типа std :: size_t. Тот
аргумент должен быть не меньше размера создаваемого объекта;
он может быть больше размера создаваемого объекта, только если
Объект является массивом.

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

Placement new возвращает переданный указатель (см. § 18.6.1.3/2) как результат «размещения», поэтому представление объекта построенного объекта начнется с этого адреса.

  • static_cast<> и неявные преобразования между T* тип и void* преобразовать между указателем на объект и указателем на его хранилище, если объект является законченным объектом.

§4.10 / 2 говорит:

Значение типа «указатель на cv T», где T — тип объекта, может быть
преобразован в значение типа «указатель на cv void». Результат
преобразование «указателя на cv T» в «указатель на cv void» указывает на
начало места хранения, где находится объект типа T, как
если объект является наиболее производным объектом (1.8) типа T […]

Это определяет неявное преобразование для преобразования, как указано. Далее §5.2.9 [expr.static.cast] / 4 определяет static_cast<> для явных преобразований, где существует неявное преобразование, которое имеет тот же эффект, что и неявное преобразование:

В противном случае выражение e может быть явно преобразован в тип T
используя static_cast формы static_cast<T>(e) если декларация
T t(e); правильно сформирован, для некоторой придуманной временной переменной t (8.5).
Эффект такого явного преобразования такой же, как выполнение
объявление и инициализация, а затем с использованием временного
переменная в результате преобразования. […]

Для обратного static_cast<> (от void* в T*), §5.2.9 / 13 гласит:

Prvalue типа «указатель на cv1 void» может быть преобразовано в prvalue
типа «указатель на cv2 T», где T — это тип объекта, а cv2 — это
та же квалификация cv или более высокая квалификация cv1. […] Значение указателя типа на объект, преобразованное в «указатель на cv void»
и обратно, возможно, с другим cv-квалификацией, будет иметь
первоначальная стоимость

Так что если у вас есть void* указывая на хранение T объект (который является значением указателя, который будет результатом неявного преобразования T* к объекту, то static_cast этого к T* выдаст действительный указатель на объект.

Возвращаясь к вашему вопросу, предыдущие пункты подразумевают, что если у вас есть

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
void * pvt_ = &t_;

T* pT = new (&t_) T(args...);
void * pvT = pT;

затем

  • хранение *pT точно перекрывает первый размер (T) байтов хранилища t_, чтобы pvT == pvt_
  • pvt_ == static_cast<void*>(&t_)
  • static_cast<T*>(pvT) == pT
  • Взятые вместе, что дает static_cast<T*>(static_cast<void*>(&t_)) == pT
13

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

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

По вопросам рекламы [email protected]