Подтвердить что ты не робот

Почему определение стандартного макета С++ 11 POD так оно и есть?

Я изучаю новое, расслабленное определение POD в С++ 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?

Наконец, что пошло бы неправильно, если бы у более чем одного составного класса были члены данных?

4b9b3361

Ответ 1

Вам разрешено указывать стандартный адрес объекта класса макета указателю на его первый член и обратно на один из последующих абзацев, который также часто выполняется в 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 или нет),

Наконец, что пошло бы неправильно, если бы у более чем одного составного класса были члены данных?

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

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

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

Ответ 2

В основном это касается совместимости с С++ 03 и C:

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

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

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

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

Ответ 3

Что пойдет не так, если мы допустим данные с различными контрольными элементами доступа?

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

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 s. Это фактически ослабление правил.

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

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