Почему определение «стандартного макета» POD в C ++ 11 таково, как оно есть? - PullRequest
41 голосов
/ 23 августа 2011

Я смотрю на новое, упрощенное определение POD в C ++ 11 (раздел 9.7)

Класс стандартной компоновки - это класс, который:

  • не имеет нестатических членов-данных типа нестандартного класса макета (или массива таких типов) или ссылки,
  • не имеет виртуальных функций (10.3) и виртуальных базовых классов(10.1),
  • имеет одинаковый контроль доступа (пункт 11) для всех нестатических элементов данных,
  • не имеет базовых классов нестандартной компоновки,
  • либо не имеет нестатических элементов данных в наиболее производном классе и не более одного базового класса с нестатическими элементами данных , либо не имеет базовых классов с нестатическими элементами данных, и
  • не имеет базовых классов того же типа , что и первый нестатический элемент данных .

Я выделил биты, которые меня удивили.

Что бы пошло не так, если бы мы допускали элементы данных с различными элементами управления доступом?

Что бы пошло не так, если быпервый член данных был также базовым классом?то есть

struct Foo {};
struct Good : Foo {int x; Foo y;};
struct Bad  : Foo {Foo y; int x;};

Я признаю, что это странная конструкция, но почему Bad должно быть запрещено, а не Good?

Наконец, что может пойти не так, если бы более одного составляющего классачлены данных?

Ответы [ 6 ]

23 голосов
/ 23 августа 2011

Это в основном совместимость с C ++ 03 и C:

  • тот же контроль доступа - в реализациях C ++ 03 разрешено использовать спецификаторы контроля доступа как возможность переупорядочить (группы) членов класса, например, чтобы упаковать его лучше.
  • более одного класса в иерархии с нестатическими членами данных - C ++ 03 не говорит, где расположены базовые классы или дополняется ли заполнение в подобъектах базового класса, которые будут присутствовать в законченном объекте того же типа.
  • базовый класс и первый член того же типа - из-за второго правила, если тип базового класса используется для члена данных, тогда он должен быть пустым классом. Многие компиляторы реализуют пустую оптимизацию базового класса, поэтому то, что Андреас говорит о подобъектах с одинаковым адресом, было бы правдой. Я не уверен, хотя то, что это о классах стандартной компоновки, означает, что для подобъекта базового класса плохо иметь тот же адрес, что и у первого члена данных того же типа, но не имеет значения, когда подобъект базового класса имеет тот же адрес, что и у первого элемента данных другого типа. [Правка: это потому, что разные объекты одного типа имеют разные адреса, даже если они являются пустыми подобъектами. Благодаря Йоханнесу]

C ++ 0x, вероятно, мог бы определить, что эти вещи также являются типами стандартной компоновки, и в этом случае он также определит, как они расположены, в той же степени, что и для стандартных. Типы макетов. Далее в ответ Йоханнеса. Посмотрите на его пример замечательного свойства классов стандартной компоновки, которому мешают эти вещи.

Но если бы это было так, то некоторые реализации были бы вынуждены изменить то, как они планируют классы, чтобы соответствовать новым требованиям, что является помехой для структурной совместимости между различными версиями этого компилятора до и после C ++. 0x. В основном это нарушает C ++ ABI.

Мое понимание того, как была определена стандартная компоновка, заключается в том, что они смотрели на то, какие требования POD можно было ослабить, не нарушая существующие реализации. Поэтому я предполагаю, не проверяя, что выше приведены примеры, когда некоторые существующие реализации C ++ 03 действительно используют не-POD-природу класса, чтобы сделать что-то, что несовместимо со стандартной компоновкой.

22 голосов
/ 23 августа 2011

Вам разрешено приводить адрес объекта класса стандартного макета к указателю на его первый член и обратно одним из более поздних абзацев, что также часто делается в C:

struct A { int x; };
A a;

// "px" is guaranteed to point to a.x
int *px = (int*) &a;

// guaranteed to point to a
A *pa = (A*)px; 

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

И, наконец, что может пойти не так, если бы более одного составного класса имели элементы данных?

Внутри класса члены распределяются по возрастающим адресам в соответствии с порядком объявления.Однако C ++ не определяет порядок распределения элементов данных по классам.Если и у производного класса, и у базового класса есть элементы данных, Стандарт не определяет преднамеренно порядок их адресов, чтобы обеспечить полную гибкость реализации в макетировании памяти.Но чтобы приведенное выше приведение в действие сработало, вам необходимо знать, что является «первым» членом в порядке выделения!

Что может пойти не так, если первый элемент данных также будет базовым классом?

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

7 голосов
/ 23 августа 2011

Что бы пошло не так, если бы мы допускали элементы данных с различными элементами управления доступом?

Текущий язык говорит, что компилятор не может переупорядочивать элементы под одним и тем же элементом управления доступом.Например:

struct x
{
public:
    int x;
    int y;
private:
    int z;
};

Здесь x должен быть выделен перед y, но нет ограничений на z относительно x и y.

struct y
{
public:
    int x;
public:
    int y;
};

Новая формулировка гласит, что yвсе еще POD, несмотря на два public с.Это на самом деле ослабление правил.

4 голосов
/ 23 августа 2011

Относительно того, почему Bad не разрешено, позвольте мне привести цитату из статьи, которую я нашел:

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

http://www.open -std.org / jtc1 / sc22 / wg21 / docs /apers / 2007 / n2172.html

2 голосов
/ 23 августа 2011

Из пункта 5 кажется, что оба они не являются модулями, так как наиболее производный класс имеет элемент не статических данных (int), он не может иметь базовый класс с элементом не статических данных.

Я понимаю это как: «только один из« базовых »классов (то есть сам класс или один из классов, от которых он наследуется) может иметь нестатические члены-данные»

1 голос
/ 23 августа 2011

struct Good также не является стандартным макетом, поскольку Foo и Good имеют нестатический элемент данных.

Таким образом, Good должно быть:

struct Foo {int foo;};
struct Good : public Foo {Foo y;};

, что не удаетсядля удовлетворения 6-й пули.Отсюда 6-я пуля?

...