Выполняют ли С++ 11 делегированные ctors хуже, чем С++ 03, вызывающие функции init?

[этот вопрос был сильно отредактирован; прошу прощения, я переместил изменения в ответ ниже]

Из Википедия (включая субчастицу) на С++ 11:

Эта [новая функция конструкторов делегирования] имеет оговорку: С++ 03 рассматривает объект, который должен быть сконструирован, когда его конструктор заканчивает выполнение, но С++ 11 рассматривает объект, построенный после завершения любого конструктора. Поскольку разрешено выполнение нескольких конструкторов, это будет означать, что каждый конструктор делегирования будет выполняться на полностью построенном объекте своего собственного типа. Конструкторы производных классов будут выполняться после завершения делегирования в базовых классах. "

Означает ли это, что цепочки делегирования создают уникальный временный объект для каждой ссылки в цепочке делегирования ctor? Подобные накладные расходы, чтобы избежать простого определения функции init, не стоили бы дополнительных накладных расходов.

Отказ от ответственности: я задал этот вопрос, потому что я студент, но ответы до сих пор были неверными и демонстрируют отсутствие исследований и/или понимания исследований, на которые ссылаются. Я был несколько разочарован этим, и в результате мои изменения и комментарии были поспешно и плохо составлены, в основном по смартфону. Прошу прощения; Надеюсь, что я минимизировал это в своем ответе ниже, и я узнал, что мне нужно быть осторожным, полным и ясным в своих комментариях.

Ответ 1

Нет. Они эквивалентны. Конструктор делегирования ведет себя как обычная функция-член, действующая на объект, построенный предыдущим конструктором.

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

В разделе 4.3 - Изменения в §15, предлагаемое изменение стандартных состояний:

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

Это означает, что конструктор делегирования работает на полностью построенном объекте (в зависимости от того, как вы его определяете) и позволяет реализации иметь делегирование ctors как функции-члены.

Ответ 2

Конструкторы классов имеют две части, список инициализаторов членов и тело функции. При делегировании конструктора сначала запускается список инициализатора и тело функции делегированного (целевого) конструктора. После этого выполняется тело функции конструктора делегирования. Вы можете в определенных случаях рассмотреть объект, который будет полностью построен, когда выполняются как список инициализаторов, так и тело функции какого-либо конструктора. Вот почему wiki говорит, что каждый конструктор делегирования будет выполняться на полностью построенном объекте своего типа. На самом деле семантика может быть более точно описана как:

... тело функции, каждый конструктор делегирования будет выполняться на полностью построенном объекте собственного типа.

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

В любом случае, поскольку выполняется только один список инициализаторов, таких накладных расходов, как вы упомянули, не существует. Ниже приводятся цитаты из cppreference:

Если имя самого класса отображается как класс или идентификатор в список инициализаторов членов, то список должен состоять из одного члена только инициализатор; такой конструктор известен как делегирование конструктор и конструктор, выбранный единственным членом Список инициализаторов - это целевой конструктор

В этом случае целевой конструктор выбирается перегрузкой разрешения и выполняется сначала, тогда управление возвращается к делегирующий конструктор и его тело.

Делегирующие конструкторы не могут быть рекурсивными.

Ответ 3

Связанные конструкторы делегирования в С++ 11 несут больше накладных расходов, чем стиль функции init С++ 03!

См. стандартную черновик С++ 11 N3242, раздел 15.2. Исключение может происходить в блоке выполнения любой ссылки в цепочке делегирования, а С++ 11 расширяет существующее поведение обработки исключений для учета этого.

[текст] и выделение.

Объект любой продолжительности хранения, инициализация или уничтожение которой заканчивается исключением, будет иметь деструкторы, выполняемые для всех полностью построенных подобъектов..., то есть для подобъектов, для которых завершен главный конструктор (12.6.2) исполнение и деструктор еще не начали выполнение. Аналогично, если конструктор без делегирования для объекта завершил выполнение, а конструктор делегирования для этого объекта завершится с исключением, будут вызваны объекты [обработанные как подобъект, как указано выше].

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

Мне приходилось разбираться в таких вещах, как, например, как стек работает на уровне аппаратного обеспечения, что такое указатель стека, какие автоматические объекты и каковы стеки, чтобы действительно понять, как это работает. Технически эти термины/понятия представляют собой конкретные детали реализации, поэтому N3242 не определяет ни одно из этих условий; но он их использует.

Суть его: объекты, объявленные в стеке, выделяются в память, а исполняемый файл обрабатывает адресацию и очистку для вас. Реализация стека была простой в C, но в С++ у нас есть исключения, и они требуют расширения рассылки C-стека. Раздел 5 документ Страуступа * обсуждает необходимость разматывания расширенных стеков и дополнительные дополнительные накладные расходы, вносимые такой функцией:

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

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

Это правда, что накладные расходы минимальны, и я уверен, что правильные реализации оптимизируют простые случаи для удаления этих служебных данных. Однако рассмотрим случай, когда у вас есть цепочка наследования класса 5. Пусть говорят, что каждый из этих классов имеет 5 конструкторов, и в каждом классе эти конструкторы называют друг друга в цепочке, чтобы уменьшить избыточное кодирование. Если вы создаете экземпляр самого производного класса, вы понесете вышеописанные накладные расходы до 25 раз, тогда как версия С++ 03 понесла бы это накладные расходы до 10 раз. Если вы сделаете эти классы виртуальными и многократно наследуемыми, эти накладные расходы будут увеличиваться, связанные с накоплением этих функций, а также те функции, которые сами вводят дополнительные накладные расходы. Нравственность здесь заключается в том, что по мере того, как ваш код масштабируется, вы почувствуете укус этой новой функции.

* Ссылка Stroustrup была написана давно, чтобы мотивировать обсуждение обработки исключений С++ и определяет потенциальные (не обязательно) возможности языка С++. Я выбрал эту ссылку для конкретной ссылки на конкретную реализацию, потому что она понятна для человека и "переносима". Мое основное использование этой статьи - раздел 5: в частности, обсуждение необходимости раскручивания стека С++ и необходимость его накладных расходов. Эти понятия легитимируются в документе и действительны сегодня для С++ 11.