reinterpret_cast для данных почти стручка (достаточно ли совместимости с макетом) - PullRequest
8 голосов
/ 21 февраля 2011

Я пытаюсь узнать о static_cast и reinterpret_cast.

Если я прав, стандарт (9.2.18) говорит, что reinterpret_cast для данных стручка безопасен:

Указатель на объект POD-struct, соответствующим образом преобразованы с использованием reinterpret_cast, указывает на его первоначальный член (или если этот член битовое поле, то к единице, в которой оно проживает) и наоборот. [Примечание: поэтому может быть безымянным заполнение внутри объекта POD-struct, но не в его начале, как это необходимо для достижения соответствующее выравнивание. - конец примечание]

У меня вопрос, как строго это интерпретировать. Достаточно ли, например, совместимости с макетом? а если нет, то почему?

Для меня в следующем примере показан пример, где строгая интерпретация «только POD действительна», по-видимому, неверна.

class complex_base  // a POD-class (I believe)
{
public:  
  double m_data[2];
};

class complex : public complex_base
{  //Not a POD-class (due to constructor and inheritance)
public:
  complex(const double real, const double imag); 
}

double* d = new double[4];
//I believe the following are valid because complex_base is POD
complex_base& cb1 = reinterpret_cast<complex_base&>(d[0]);  
complex_base& cb2 = reinterpret_cast<complex_base&>(d[2]);
//Does the following complete a valid cast to complex even though complex is NOT POD?
complex& c1 = static_cast<complex&>(cb1);
complex& c2 = static_cast<complex&>(cb2);

Кроме того, что может сломаться, если complex_base::m_data защищен (что означает, что complex_base не является модулем)? [РЕДАКТИРОВАТЬ: и как мне защитить себя / обнаружить такие поломки]

Мне кажется, что совместимости с макетом должно быть достаточно, но это не то, что говорит стандарт.

EDIT: Спасибо за ответы. Они также помогли мне найти это, http://www.open -std.org / ОТК1 / SC22 / wg21 / документы / документы / 2007 / n2342.htm

Ответы [ 2 ]

5 голосов
/ 21 февраля 2011

Я считаю, что следующие условия действительны, так как complex_base - POD

Вы не правы.d[0] не относится к первому члену complex_base объекта.Поэтому его выравнивание может быть недостаточно хорошим для объекта complex_base, поэтому такое приведение небезопасно (и не допускается цитируемым вами текстом).

Выполняет ли следующее допустимое приведение к комплексу, даже если комплекс НЕ является POD?

cb1 и cb2 не указывают на подобъекты объектавведите complex, поэтому static_cast вызывает неопределенное поведение.Обратитесь к 5.2.9p5 C ++ 03

Если lvalue типа "cv1 B" на самом деле является подобъектом объекта типа D, lvalue ссылается на включающий объект типаD. В противном случае результат приведения не определен.

Недостаточно, если совпадают только участвующие типы.В тексте говорится о указателе, указывающем на объект POD-struct, и о lvalue, ссылающемся на определенный подобъект.Complex и complex_base являются объектами стандартной компоновкиСпецификация C ++ 0x гласит: вместо текста, который вы цитировали:

Является ли требование POD-ness слишком строгим?

Это другой вопрос, не касающийся вашего примера кода.Да, требование POD-Ness слишком строго.В C ++ 0x это было признано, и дано новое требование, которое является более свободным, "стандартная компоновка".Я думаю, что и complex , и complex_base - это классы стандартной компоновки, по определению C ++ 0x.В спецификации C ++ 0x вместо цитируемого вами текста сказано:

Указатель на объект структуры стандартной компоновки, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный член (или, если этот членбитовое поле, затем к единице, в которой он находится), и наоборот.

Я интерпретирую это как разрешение привести указатель к double, который фактически указывает на член complex (член по наследству), который должен быть приведен к complex*.Класс компоновки Standard - это класс, который либо не имеет базовых классов, содержащих нестатические данные, либо имеет только один базовый класс, содержащий нестатические данные.Таким образом, существует уникальный «начальный член».

1 голос
/ 21 февраля 2011

Что может сломать, так это то, что экземпляры не-POD класса могут иметь указатели vtable для реализации виртуальной диспетчеризации, если у них есть какие-либо виртуальные функции, включая виртуальный dtor.Указатель vtbl обычно будет первым членом не POD-класса.

(Технически виртуальная диспетчеризация не должна быть реализована таким образом; практически так и есть. Поэтому стандарт должен быть настолько строг в отношении того, что квалифицируется как тип POD.)

I 'Я, честно говоря, не уверен, почему просто наличие ctor («8.5.1 (1):« Агрегат - это массив или класс (раздел 9) без объявленных пользователем конструкторов (12.1) »), что не признает POD. Но,это так.

В конкретном случае, который у вас здесь есть, разумеется, нет необходимости переинтерпретировать приведение. Вместо этого просто добавьте оператор преобразования в базовый класс:

class complex_base  // a POD-class (I believe)
{
public:  
  double m_data[2];
  operator double*() {
    return m_data;
  }
};


complex_base b; // create a complex_base
double* p = b; 

complex_base не является double *, компилятор C ++ будет применять один (и только один) пользовательский оператор преобразования, чтобы назначить b для p. Это означает, что p = b вызывает оператор преобразования, давая p = b.operator double*() (иобратите внимание, что это на самом деле допустимый синтаксис - вы можете напрямую вызывать операторы преобразования, а не то, что вы должны), который, конечно, делает все, что он делает, в этом случае возвращает m_data.

Обратите внимание, что это яs сомнительно, так как теперь у нас есть прямой доступ к внутренностям b.На практике мы можем вернуть const double *, или копию, или умный «указатель» при копировании при записи, или ....

Конечно, в этом случае m_data в любом случае общедоступна, поэтомумы ничуть не хуже, чем если бы мы только что написали:

 double* p = b.m_data;

На самом деле мы немного лучше, потому что клиентам complex_base не нужно знать как , чтобы преобразовать егов два раза.

...