2015-02-07 4 views
7

В варианте класса я работаю необработанного хранения является массив символов:reinterpret_cast/static_cast и неопределенное поведение

alignas(/* the strictest alignment of all types of the variant */) 
char storage[/* ... */]; 

Оператор присваивания является что-то вроде:

template<class X> 
void operator=(const X &x) 
{ 
    // ...code for clearing the storage and setting the tag for type X... 

    new(storage) X(x); 
} 

а код для получения сохраненного объекта:

template<class X> 
const X &get() 
{ 
    // ... 
    return *reinterpret_cast<X *>(storage); 
    // ... 
} 

Кажется, что это работает, но всегда ли оно определено? Я беспокоюсь о безопасном разыменовании указателя (допустимо ли это правилами псевдонимов типа?).

Существует ли какое-либо различие между текущей реализацией и

return *static_cast<const X *>(static_cast<const void *>(storage)); 

Связанным вопросом/ответ:

https://stackoverflow.com/a/7321815/3235496 (см James Kanze «s комментариев).


EDIT

Второй вопрос уже есть ответ здесь: C++ When should we prefer to use a two chained static_cast over reinterpret_cast

+0

Несомненно, это «новое (хранилище) X (x);» - утечка памяти –

+1

@EdHeal. Он создает X в хранилище с помощью размещения new. Это должно быть безопасная практика для выровненного хранилища (например, http://stackoverflow.com/questions/4583125/char-array-as-storage-for-placement-new). Не могли бы вы добавить некоторые детали? – manlio

ответ

4

Как storage правильно выровнены, я не могу себе представить, где может возникнуть проблема. В абзаце (*) 4.10 о конверсиях указателей говорится: Значение типа «указатель на cv T», где T - тип объекта, может быть преобразовано в prvalue типа «указатель на cv void». Результат преобразования значения ненулевого указателя указателя на тип объекта на «указатель на cv void» представляет адрес того же байта в памяти, что и исходное значение указателя.

Что касается вашего второго вопроса, пункт 5.2.10 на reinterpres_cast: указатель на объект может быть явно преобразован в указатель на объект другого типа. Когда prvalue v типа указателя объекта преобразуется в тип указателя объекта «указатель на cv T», результатом является static_cast<cv T*>(static_cast<cv void*>(v)), где cv обозначает необязательный const или volatile.

Таким образом, эта часть гарантируется по спецификациям. Больше, как мы видели, что приведение к void * должен указывать на первый байт памяти, нет UB для моего понимания стандартных ... при условии, компиляторы же понимание ;-)

(*) Référence: Draft for current C++ specification