在副本中,至少我的问题的第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 Aggregates中没有此类信息。但是按照这个
当聚合由初始化列表初始化时,如 8.5.4 中所指定,初始化列表的元素被视为聚合成员的初始化,按递增的下标或成员顺序。每个成员都是从相应的 initializer-clause 复制初始化的。. . (#3367, 8.5.1)
我可以假设构造函数 [编译器在聚合的情况下生成] 在聚合初始化期间只是被忽略了。所以我可以假设删除的默认构造函数也被简单地忽略了,所以C obj { }; 应该可以成功编译,但对我来说,使用已删除的默认构造函数创建对象很奇怪。尽管如此,如果我理解正确,根据这个版本的标准 obj1-case 是可以的,而 obj2,obj3-cases 无法编译是错误的。我对吗?
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.在我看来,新的值初始化定义很奇怪,因为我想不出任何情况下我们可以使用已删除的默认构造函数创建和值初始化对象。我的意思是,例如,如果我将C 类的int c成员设为私有,因此 C 类不再是表达式的聚合
C obj1 { };
将是值初始化(不是以前的聚合初始化)[#3367, 8.5.4 "List-initialization"] 并且肯定无法编译。你能解释一下在新的值初始化定义中删除构造函数的那一刻吗?
我知道这里有很多文字。如果您回答了一些问题,我将非常感激。