2016-05-14 6 views
0

IsoCpp.org предлагает FAQ относительно размещения нового:Строгие Aliasing Правило и размещение Нового

примера они обеспечивают:

#include <new>  // Must #include this to use "placement new" 
#include "Fred.h"  // Declaration of class Fred 
void someCode() 
{ 
    char memory[sizeof(Fred)];  // Line #1 
    void* place = memory;   // Line #2 
    Fred* f = new(place) Fred(); // Line #3 (see "DANGER" below) 
    // The pointers f and place will be equal 
    // ... 
} 

бы не выше код нарушает строгое правило сглаживания C++ 's начиная с place и memory - это разные типы, но ссылки на одну и ту же ячейку памяти?

(я знаю, что указатели типа char может псевдоним любого другого типа, но здесь мы, кажется, есть void* альясинга в char*, который не имеет права от того, что я понимаю?)

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

Спасибо

+0

Стандарт фактически делает исключение для указателей 'char' в строгом сглаживании. Поскольку они используются так часто, чтобы сделать буферы точного размера для хранения различных структур и еще чего-то, компиляторы должны предполагать, что любой другой указатель может иметь псевдоним 'char *'. Учитывая, что 'void *' не может быть отсрочен, в общем случае компиляторы будут жаловаться только на то, что если вы укажете указатель void на другой тип и используете его, вы можете столкнуться с проблемами псевдонимов. Ваша основная проблема - сглаживание 'memory' и' f', но при условии, что 'memory' является' char * ', вы в порядке. – RyanP

+0

@ RyanP, спасибо за разъяснение; Я совершенно новичок в строгом псевдониме и не понимал, что на самом деле нужно разыгрывать UB. Я знал об исключении 'char *' aliasing, но я думал, что он пошел только в одну сторону. Значит, 'char *' может использовать псевдоним (и deref) любого типа 'T', но тип' T' может быть только псевдонимом и deref тип 'char *', если 'T' сам имеет тип' char * '? – digitale

ответ

2

Что такое правильный способ соблюдать строгое правило наложения спектров при использовании размещения нового?

Правильный способ заключается в использовании std::aligned_storage. Этот образец кода не гарантирует правильное выравнивание хранилища для Fred, поэтому его не следует использовать.

Правильный способ сделать это:

#include <new>   // For placement new 
#include <type_traits> // For std::aligned_storage 

struct Fred { 
    // ... 
}; 

void someCode() { 
    std::aligned_storage<sizeof(Fred), alignof(Fred)>::type memory; 
    // Alternatively, you can remove the "alignof(Fred)" template parameter if you 
    // are okay with the default alignment, but note that doing so may result in 
    // greater alignment than necessary and end up wasting a few bytes. 
    Fred* f = new(&memory) Fred(); 
} 

Не было бы выше код нарушает строгое правило сглаживания C++ 's, так как место памяти и различные типы, но ссылаться на ту же ячейку памяти?

Теперь, как для озабоченности по поводу наложения спектров между f, place и memory в исходном коде, обратите внимание, что не любое нарушение сглаживания. Строгое правило псевдонимов означает, что вы не можете «dereference a pointer that aliases an incompatible type». Так как вы не можете разыскивать void* (и законно конвертировать указатель в/из void*), нет никакого риска place, что приведет к нарушению строгой алиасии.

+0

Если вы не думаете о каком-то особом исключении в стандарте, который позволяет сглаживать с помощью 'std :: aligned_storage :: type', это не отвечает на вопрос, это должен быть просто комментарий. – hvd

+0

@hvd: Первая половина моего ответа предназначена для того, чтобы охватить «Каков правильный способ соблюдения правила строгого сглаживания при использовании нового места размещения?» вопрос с демонстрацией того, как правильно использовать новое место размещения. Но вы правы, что я не рассматривал другие части вопроса, поэтому я добавил вторую половину своего ответа. – Cornstalks

+0

Это выглядит нехорошо, но я не думаю, что это совершенно правильно: независимо от того, является ли преобразование указателя действительным, не зависит от того, можно ли разыменовать результирующий указатель. Может существовать нарушение псевдонимов, если какой-либо код ссылается на байты в 'memory' напрямую. Но если этого не происходит, нет наложения псевдонимов, поэтому также не может быть нарушения псевдонимов. – hvd

4

Нет проблем, поскольку код не относится к *place. То, что указатели равны, не вызывает UB - это косвенное использование обоих из них, что запрещено.

Например, следующее является законным:

struct A {int x;} a; 
struct B {} *pb = reinterpret_cast<B*>(&a); 
A* pa = reinterpret_cast<A*>(pb); 

Обратитесь к *pb и вы нарушили строгие правила наложения спектров.

В вашем конкретном примере, конечно, вы не может написать *place потому, что дало бы в объект типа void, не допускается.

Отметим также точка кукурузные стебли делает: Пример действительно нужно использовать std::aligned_storage, потому что нет никакой гарантии, что memory правильно выровнен для Fred объекта.На практике это часто не имеет значения, потому что вы выделили память для нового места с чем-то вроде new или malloc (которые возвращают подходящую выровненную память).

+0

Также важно, чтобы не было кода, который напрямую ссылается на байты 'memory'. – hvd

+0

Я * думаю *, что будет разрешено - из-за специальных правил, управляющих символом, - но это было бы хорошим стилем, чтобы избежать этого. –

+0

Благодарим вас за то, что вы заметили, что для нарушения должно быть нарушение правил строгого сглаживания – digitale

Смежные вопросы