Действительно ли безопасно вводить reinterpret_cast в std:: unique_ptr?
При использовании различных API, которые имеют структуры с переменным размером (структуры, которые должны быть выделены как byte [], а затем отбрасываются в структуру), было бы неплохо, если бы владелец unique_ptr мог указать на структуру, поскольку это то, что мы будем использовать.
Пример:
std::unique_ptr<VARIABLE_SIZE_STRUCT[]> v;
v.reset(reinterpret_cast<VARIABLE_SIZE_STRUCT*>(new BYTE[bytesRequired]));
Это позволило `v предоставить представление самой структуре, что предпочтительнее, потому что нам не нужна вторая переменная, и мы не заботимся о указателе байта, кроме удаления.
Проблема заключается в возможности размытия указателя на литье (что делает его небезопасным для свободного). Я не вижу разумной причины, по которой компилятор изменил бы значение указателя на литье (так как наследования нет), но я слышал, что стандарт оставляет за собой право разыгрывать любой указатель на любой актерский состав, так как это касается стандартного кодирования, это подход выходит из окна, не так ли? Или есть какая-то причина, это безопасно? Есть ли способ, по крайней мере, static_assert, или каким-либо другим способом сделать его безопасным или чисто связанным с этим типом структуры?
Ответы
Ответ 1
-
ваше распределение может не иметь выравнивания, необходимого для VARIABLE_SIZE_STRUCT
-
в выделенной памяти не было объекта VARIABLE_SIZE_STRUCT placement- new
ed в нем - если вы позаботитесь об этом, логика деструктора по умолчанию unique_ptr
должна найти объект ожидаемого объекта для уничтожения, но само освобождение не было бы выполнено с помощью delete []
на BYTE*
- чтобы определить поведение, которое вам пришлось бы настроить, для того, чтобы вызывать первый ~VARIABLE_SIZE_STRUCT()
, затем delete[]
...
Если вас беспокоит "thunking", вы можете сделать проверку во время выполнения:
BYTE* p;
v.reset(reinterpret_cast<VARIABLE_SIZE_STRUCT*>(p = new BYTE[bytesRequired]));
assert(reinterpret_cast<BYTE*>(v.get()) == p);
Фон на этом - 5.2.10/7:
Указатель объекта может быть явно преобразован в указатель объекта другого типа. Когда prvalue v типа указателя объекта преобразуется в тип указателя объекта "указатель на cv T", результатом является static_cast<cvT*>(static_cast<cv void*>(v))
. Преобразование prvalue типа "указатель на T1" в тип "указатель на T2" (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не являются более строгими, чем те из T1) и обратно к исходному типу дает исходное значение указателя.
Итак, если требования к выравниванию для VARIABLE_SIZE_STRUCT
более строгие, чем для BYTE
, вам не гарантируется получение исходного указателя с помощью reinterpret_cast<BYTE*>
.
Ответ 2
Вы правы, это небезопасно. Однако можно сделать это безопасным.
Стандарт гарантирует, что если вы reinterpret_cast
на другой тип, затем вернитесь к исходному типу, вы вернете исходное значение.
Вы можете использовать это вместе с пользовательским удалением, чтобы убедиться, что ваш внутренний указатель возвращается к типу, который он был назначен, прежде чем освобождать его.
auto deleter = [](VARIABLE_SIZE_STRUCT* ptr)
{
delete[] reinterpret_cast<uint8_t*>(ptr);
};
std::unique_ptr<VARIABLE_SIZE_STRUCT, decltype(deleter)> v
(reinterpret_cast<VARIABLE_SIZE_STRUCT*>(new uint8_t[256]), deleter);
На этом этапе вам, вероятно, лучше создать собственную оболочку и не использовать unique_ptr
.