Инициализация класса с удаленным конструктором по умолчанию в разных стандартных версиях С++ 11

В дубликате нет ответа как минимум на пункты 1 и 4 моего вопроса. И они самые важные. Остальные пункты могу удалить, но прошу не закрывать весь вопрос.

1. В приведенном ниже коде obj1 создается нормально. Но если я попытаюсь раскомментировать творения obj2 и obj3, компиляция (-std=c++11, g++ 4.9.2) завершится ошибкой. Почему это так? Я думал, что инициализация должна выполняться одинаково независимо от того, была ли выделена память объекта в стеке или в куче.


struct C
{
    int c;

    C() = delete;
};

int main()
{   
    C obj1 { };

    C *obj2 = (C *) malloc(sizeof(C));
    //new ((void *) obj2) C{ };

    //C* obj3 = new C{ };

    return 0;
}

2. Я попытался выяснить, какой из двух типов поведения (obj1-case или obj2, obj3-cases) является правильным в соответствии со стандартом. В стандарте сказано (#3242 и #3337, 8.5.4):

Список-инициализация объекта или ссылки типа T определяется следующим образом:

— Если в списке инициализаторов нет элементов и T является типом класса с конструктором по умолчанию, объект инициализируется значением.

Хорошо. Итак, я перехожу к определению инициализации значения (#3242 и #3337, 8.5.0):

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

Согласно (#3242, 12.1)

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

поэтому удаленный конструктор по умолчанию является нетривиальным, и поэтому код C obj1 { }; не должен компилироваться.

Но согласно (#3337, 12.1)

Конструктор по умолчанию тривиален, если он не предоставляется пользователем и если: . . .

и поэтому удаленный конструктор по умолчанию тривиален, код C obj1 { }; должен успешно скомпилироваться.

Где правда?

3. Но это еще не все. В следующей версии стандарта сказано (#3367, 8.5.4):

Список-инициализация объекта или ссылки типа T определяется следующим образом:

— Если T является агрегатом, выполняется агрегатная инициализация

— В противном случае, если в списке инициализаторов нет элементов и T является типом класса с конструктором по умолчанию, объект инициализируется значением.

Насколько я понимаю, C — это совокупность. Но тут у меня проблема: я не нашел инфы о том, как создается агрегат с удаленным дефолтным конструктором. В разделе 8.5.1 Агрегаты такой информации нет. Но согласно этому

Когда агрегат инициализируется списком инициализаторов, как указано в 8.5.4, элементы списка инициализаторов берутся в качестве инициализаторов для членов агрегата в порядке возрастания индекса или членов. Каждый член инициализируется копированием из соответствующего предложения инициализатора. . . (#3367, 8.5.1)

Я могу предположить, что конструкторы [генерируемые компилятором в случае агрегатов] просто игнорируются во время агрегатной инициализации. Поэтому я могу предположить, что удаленный конструктор по умолчанию также просто игнорируется, и поэтому C obj { }; должен быть успешно скомпилирован, хотя для меня странно создавать объект с удаленным значением по умолчанию. конструктор. Тем не менее, если я правильно понимаю, в соответствии с этой версией стандартного obj1-case все в порядке, и это неправильно, что obj2, obj3-cases не компилируются. Я прав?

4. В любом случае возникает логичный вопрос: на какую стандартную версию #3242/#3337 или #3367 следует полагаться? Версия #3367 была сделана в 2012 году, то есть позже 2011 года, и я не знаю, можно ли ее назвать c++11. Какая версия считается настоящим стандартом С++ 11? Я скомпилировал приведенный выше пример кода, используя g++ 4.9.2. Какой стандартный вариант использует компилятор, как я могу узнать? Потому что версии #3337 или #3367 сильно отличаются.

Например, в #3367 определение инициализации значения было кардинально изменено:

Инициализация значения объекта типа T означает:

- если T является типом класса (возможно, cv-qualified) (раздел 9) либо без конструктора по умолчанию (12.1), либо с конструктором по умолчанию, который предоставляется или удаляется пользователем, то объект инициализируется по умолчанию;

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

C obj1 { };

будет инициализация значения (а не совокупная инициализация, как это было раньше) [#3367, 8.5.4 «Инициализация списка»] и определенно не сможет скомпилироваться. Не могли бы вы пояснить момент про удаленные конструкторы в новом определении инициализации значений?

Я понимаю, что здесь много текста. Если бы вы ответили на некоторые, я был бы так так так благодарен.


person JenyaKh    schedule 19.09.2016    source источник
comment
Ваш C не тривиален, потому что у него нет неудаленных конструкторов по умолчанию. Таким образом, нет объекта типа C, время жизни которого уже началось.   -  person Kerrek SB    schedule 19.09.2016
comment
C *obj2 = (C *) malloc(1); Чтооооо. Вы должны писать на C++, а не на C. Такие вещи следует делать только в классах-контейнерах, а не с помощью простого malloc   -  person Superlokkus    schedule 19.09.2016
comment
@Superlokkus: Что еще более важно, это неправильно, поскольку это не sizeof(C).   -  person Nicol Bolas    schedule 19.09.2016
comment
@NicolBolas За исключением теоретически возможной платформы, где sizeof(int) == 1, но все же это 9000, как плохой стиль.   -  person Superlokkus    schedule 19.09.2016
comment
@Superlokkus, ну тут всего две версии: с размещением new (obj2) и обычным выражением new (obj3) для более широкого обзора ситуации. Почему нет.   -  person JenyaKh    schedule 19.09.2016
comment
@Superlokkus: Плохой стиль не имеет значения. Выполнение malloc(sizeof(C)) и создание из него C гарантированно будет допустимым C++. Выполнение malloc(1) и создание из него C не гарантирует, что это допустимый C++.   -  person Nicol Bolas    schedule 19.09.2016
comment
malloc != new! malloc не вызывает конструктор, и ему не разрешено delete, malloced память.   -  person Superlokkus    schedule 19.09.2016
comment
@NicolBolas, я просто немного отвлекся, извини.   -  person JenyaKh    schedule 19.09.2016
comment
@NicolBolas Я знаю, я просто играл в адвоката дьявола. Этот код, т. е. стиль, не имеет практического применения, насколько я могу придумать, пока   -  person Superlokkus    schedule 19.09.2016