В дубликате нет ответа как минимум на пункты 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 «Инициализация списка»] и определенно не сможет скомпилироваться. Не могли бы вы пояснить момент про удаленные конструкторы в новом определении инициализации значений?
Я понимаю, что здесь много текста. Если бы вы ответили на некоторые, я был бы так так так благодарен.
C
не тривиален, потому что у него нет неудаленных конструкторов по умолчанию. Таким образом, нет объекта типаC
, время жизни которого уже началось. - person Kerrek SB   schedule 19.09.2016C *obj2 = (C *) malloc(1);
Чтооооо. Вы должны писать на C++, а не на C. Такие вещи следует делать только в классах-контейнерах, а не с помощью простогоmalloc
- person Superlokkus   schedule 19.09.2016sizeof(C)
. - person Nicol Bolas   schedule 19.09.2016sizeof(int) == 1
, но все же это 9000, как плохой стиль. - person Superlokkus   schedule 19.09.2016malloc(sizeof(C))
и создание из негоC
гарантированно будет допустимым C++. Выполнениеmalloc(1)
и создание из негоC
не гарантирует, что это допустимый C++. - person Nicol Bolas   schedule 19.09.2016malloc != new
!malloc
не вызывает конструктор, и ему не разрешеноdelete
,malloc
ed память. - person Superlokkus   schedule 19.09.2016