Ответ 1
Ясно, что
struct text_descriptor td[3] = { {0, {465,223}, 123}, {1, {465,262}, 123}, };
- это инициализация списка, а список инициализации не пуст.
С++ 11 говорит ([dcl.init.list] p3):
Список-инициализация объекта или ссылки типа
T
определяется следующим образом:
- Если в списке инициализаторов нет элементов, а
T
- это тип класса с конструктором по умолчанию, объект инициализируется значением.- В противном случае, если
T
является агрегатом, выполняется агрегатная инициализация (8.5.1).- ...
[dcl.init.aggr] р1:
Агрегат - это массив или класс (раздел 9) без конструкторов, предоставляемых пользователем (12.1), без инициализаторов скобок или равных для нестатических элементов данных (9.2), без частных или защищенных нестатических (раздел 11), нет базовых классов (раздел 10) и нет виртуальных функций (10.3).
td
- это массив, поэтому он является агрегатом, поэтому выполняется агрегатная инициализация.
[dcl.init.aggr] P7:
Если в списке меньше предложений-инициализаторов, чем в агрегате, то каждый член, явно не инициализированный, должен быть инициализирован из пустого списка инициализаторов (8.5.4).
Это здесь, поэтому td[2]
инициализируется из пустого списка инициализации, который снова ([dcl.init.list] p3) означает, что он инициализирован значением.
Значение-инициализация, в свою очередь, означает ([dcl.init] p7):
Для инициализации значения объекта типа
T
означает:
- если
T
является (возможно, cv-квалифицированным) типом класса (раздел 9) с предоставленным пользователем конструктором (12.1),...- Если
T
является (возможно, cv-квалифицированным) классом неединичного класса без конструктора, предоставленного пользователем, тогда объект инициализируется нулем и, еслиT
неявно объявленный конструктор по умолчанию является нетривиальным, этот конструктор называется.- ...
Ваш класс text_descriptor
- это класс без конструктора, предоставляемого пользователем, поэтому td[2]
сначала инициализируется нулем, а затем вызывается его конструктор.
Средство нулевой инициализации ([dcl.init] p5):
Для нулевой инициализации объекта или ссылки типа T означает:
- если
T
- скалярный тип (3.9),...- если
T
является (возможно, cv-квалифицированным) классом неединичного класса, каждый нестатический элемент данных и каждый подобъект базового класса инициализируются нулем, а заполнение инициализируется нулевыми битами;- если
T
является (возможно, cv-квалифицированным) типом объединения,...- if
T
- тип массива,...- Если
T
является ссылочным типом, инициализация не выполняется.
Это корректно определено независимо от конструктора по умолчанию text_descriptor
: он просто равен нулю - инициализирует не ссылочные члены и под-члены.
Затем вызывается конструктор по умолчанию, если он нетривиален. Здесь, как определяется конструктор по умолчанию ([special] p5):
Конструктор по умолчанию для класса
X
является конструктором классаX
, который может быть вызван без аргумента. Если для классаX
нет объявленного пользователем конструктора, конструктор без параметров неявно объявляется по умолчанию (8.4). Неявно объявленный конструктор по умолчанию является встроенным публичным членом своего класса. По умолчанию конструктор по умолчанию для классаX
определяется как удаленный, если:
- ...
- любой нестатический элемент данных без элемента с выравниванием или равным значением имеет ссылочный тип,
- ...
Конструктор по умолчанию является тривиальным, если он не предоставляется пользователем, и если:
- его класс не имеет виртуальных функций (10.3) и нет виртуальных базовых классов (10.1) и
- нет нестатического элемента данных его класса, который имеет инициализатор скобок или равный, и
- все прямые базовые классы его класса имеют тривиальные конструкторы по умолчанию, а
- для всех нестатических членов данных своего класса, которые относятся к типу класса (или его массиву), каждый такой класс имеет тривиальный конструктор по умолчанию.
В противном случае конструктор по умолчанию является нетривиальным.
Таким образом, неявно определенный конструктор удаляется, как и ожидалось, но он также является тривиальным, если pos
- тип POD (!). Поскольку конструктор тривиален, он не вызывается. Поскольку конструктор не вызывается, тот факт, что он удален, не является проблемой.
Это отверстие в С++ 11, которое с тех пор исправлено. Скорее всего, это было связано с недоступными тривиальными конструкторами по умолчанию, но исправленная формулировка также охватывает удаленные тривиальные конструкторы по умолчанию. N4140 (примерно С++ 14) говорит в [dcl.init.aggr] p7 (акцент мой):
- Если
T
является (возможно, cv-qualit) типом класса без предоставленного пользователем или удаляемого конструктора по умолчанию, тогда объект инициализируется нулем и проверяются семантические ограничения для инициализации по умолчанию, и еслиT
имеет нетривиальный конструктор по умолчанию, объект инициализируется по умолчанию;
Как T.C. отметил в комментариях, другой DR также изменился, так что td[2]
по-прежнему инициализируется из пустого списка инициализации, но этот пустой список инициализации теперь подразумевает агрегатную инициализацию. Это, в свою очередь, подразумевает, что каждый из членов td[2]
также инициализируется из пустого списка инициализации ([dcl.init.aggr] p7), поэтому, похоже, инициализирует ссылочный элемент из {}
.
[dcl.init.aggr] p9 затем говорит (как пояснил Remyabel в теперь удаленном ответе):
Если неполный или пустой список инициализаторов оставляет член ссылочного типа неинициализированным, программа плохо сформирована.
Мне непонятно, что это относится к ссылкам, инициализированным из неявного {}
, но компиляторы действительно интерпретируют его как таковое, и нет ничего другого, что могло бы означать это.