Инициализация класса с удаленным конструктором по умолчанию в различных стандартных версиях C++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-case) является правильным в соответствии со стандартом. В стандарте сказано (#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 является совокупностью. Но здесь у меня проблема: мне не удалось найти информацию о том, как создается агрегат с удаленным конструктором defult. В 8.5.1 Агрегаты такой информации нет. Но в соответствии с этим
Когда агрегат инициализируется списком инициализаторов, как указано в 8.5.4, элементы списка инициализаторов берутся в качестве инициализаторов для элементов агрегата в порядке возрастания индекса или элемента. Каждый член инициализируется копией из соответствующего предложения инициализатора.,, (№ 3367, 8.5.1)
Я могу предположить, что конструкторы [генерируемые компилятором в случае агрегатов] просто игнорируются во время инициализации агрегата. Поэтому я могу предположить, что удаленный конструктор по умолчанию также просто игнорируется и поэтому C obj { }; должен быть успешно скомпилирован, хотя для меня странно создавать объект с удаленным конструктором по умолчанию. Тем не менее, если я правильно понял, в соответствии с этой версией стандартного obj1-case все в порядке и неправильно, что obj2,obj3-case не компилируются. Я прав?
4. И в любом случае логичный вопрос: на какую стандартную версию #3242/#3337 или #3367 мне следует положиться? Версия #3367 была сделана в 2012 году, то есть позже, чем в 2011 году, и я не знаю, можно ли ее назвать C++11. Какая версия считается реальной C++11-стандартом? Я скомпилировал пример кода выше, используя g++ 4.9.2. Какой стандартный вариант использует компилятор, как я могу узнать? Потому что версии # 3337 или #3367 сильно отличаются.
Например, в #3367 определение инициализации значения было существенно изменено:
Инициализировать значение объекта типа T означает:
- если T является (возможно, cv-квалифицированным) типом класса (раздел 9) без конструктора по умолчанию (12.1) или конструктора по умолчанию, предоставленного или удаленного пользователем, то объект инициализируется по умолчанию;
5. И новое определение инициализации значения, на мой взгляд, странно, поскольку я не могу придумать ни одного случая, когда мы могли бы создать и инициализировать значение объекта с удаленным конструктором по умолчанию. Я имею в виду, например, если я сделаю int c членом класса C частным и, таким образом, C-класс перестанет быть агрегатным выражением
C obj1 { };
будет инициализацией значения (а не агрегатной инициализацией, как это было раньше) [#3367, 8.5.4 "Инициализация списка"] и определенно не сможет скомпилироваться. Не могли бы вы объяснить момент об удаленных конструкторах в новом определении инициализации значения?
Я понимаю, что здесь много текста. Если бы вы ответили, я был бы очень благодарен.