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

С++ - гарантия и имя для POD-подобных данных, memcpy

В другом question я неправильно использовал термин POD для обозначения типов данных, которые на самом деле не являются типами POD (из-за наличия конструктора). Теперь я просмотрел стандарт и не смог найти подходящее имя для того, что я хочу. Я также не могу найти гарантию того, что копирование действительно разрешено.

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

В разделе 3.9 стандарта указано, что данные POD могут быть скопированы с memcpy, либо на другой объект, либо на символьные данные и обратно. Никакая такая гарантия никогда не была сделана из данных, отличных от POD.

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

Итак, мои вопросы:

  • Действительно ли копия с memcpy гарантирована для таких объектов?
  • Если да, то почему есть специальная заметка о memcpy и POD?
  • Есть ли имя для такого типа данных, которое безопасно для memcpy?

Простым примером типа объекта я имею в виду:

struct ex_struct
{
  int a,b,c,d;
  ex_struct() : a(123) { }
}

Считая черновик С++ 0x, моя структура будет выглядеть тривиально скопируемым классом (9.1). Я считаю, что memcpy будет безопасным.

4b9b3361

Ответ 1

В С++ 0x понятие PODness разбивается на несколько индивидуально полезных категорий:

Тривиально копируемый класс - это класс, который (проект 3242, раздел [class]):

  • не имеет нетривиальных конструкторов копирования (12.8),
  • не имеет нетривиальных конструкторов перемещения (12.8),
  • не имеет нетривиальных операторов присваивания копий (13.5.3, 12.8),
  • не имеет нетривиальных операторов присваивания перемещения (13.5.3, 12.8) и
  • имеет тривиальный деструктор (12.4).

Тривиальный класс - это класс, имеющий тривиальный конструктор по умолчанию (12.1) и тривиально копируемый.

[Примечание. В частности, тривиально копируемый или тривиальный класс не имеет виртуальных функций или виртуальной базы классы. - конечная нота]

Класс стандартного макета - это класс, который:

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

Требования для тривиальных конструкторов, операторов присваивания и деструктора разбросаны по разделу 12 "Специальные функции-члены" [special].

Ответ 2

Понятие POD на С++ 03 слишком строгое. В С++ 0x POD обобщается для включения объектов, которые вы описали. Так что не волнуйтесь, вы можете назвать его POD. См. Приятное летопись на Wikipedia.

Ответ 3

Одна проблема с вашим примером заключается в том, что он имеет неявно объявленный тривиальный деструктор. Несмотря на название, реализация не запрещает AFAIK делать что-то в тривиальном деструкторе класса, отличного от POD.

Таким образом, по какой-то странной реализации ваш класс ex_struct может демонстрировать поведение во время выполнения, эквивалентное следующему:

struct weird_ex_struct
{
  int a,b,c,d;
  weird_ex_struct() : a(123), aptr(&a) { }
  weird_ex_struct(const weird_ex_struct &o) : 
    a(o.a), b(o.b), c(o.c), d(o.d), aptr(&a) {}
  weird_ex_struct &operator=(const weird_ex_struct &o) {
    a = o.a; //etc
    aptr = &a;
    return *this;
  }
  ~weird_ex_struct() {
    if (aptr != &a) std::terminate();
  }
private:
  int *aptr;
}

Я говорю о стиле выполнения, потому что weird_ex_struct имеет нетривиальный деструктор, и это влияет на то, как он может легально использоваться (не в союзах, с одной стороны). Также я думаю, что есть стандартные способы обнаружения существования частных членов данных во время компиляции. Но до тех пор, пока реализация может хранить этот секрет в секрете, если вы не сделаете что-то undefined (memcpy не-POD-объект), он затем разрешил spring удивить вас позже.

Ясно, что если weird_ex_struct скопировано с memcpy, тогда что-то странное произойдет, когда оно будет уничтожено.

Нет никакой очевидной причины для реализации, но стандартные классы, не содержащие POD, широко открыты для реализации, чтобы делать нечетные вещи. Не уверен, что это потому, что они думали, что кто-то подумает о какой-то полезной странности, или просто потому, что они не смогли определить стандартный макет, как это делает С++ 0x.

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

Однако факт остается фактом: стандарт позволяет реализациям делать много сумасшедших вещей с не-POD-объектами, и как только вы напишете конструктор, вы откроете эту дверь.]

Ответ 4

Да, это безопасно копировать с помощью memcpy, потому что конструктор только инициализирует значения.