我不明白C++ 11大括号初始化规则在这里如何工作。
具有以下代码:
struct Position_pod {
int x,y,z;
};
class Position {
public:
Position(int x=0, int y=0, int z=0):x(x),y(y),z(z){}
int x,y,z;
};
struct text_descriptor {
int id;
Position_pod pos;
const int &constNum;
};
struct text_descriptor td[3] = {
{0, {465,223}, 123},
{1, {465,262}, 123},
};
int main()
{
return 0;
}
注意,该数组被声明为具有3个元素,但仅提供了2个初始化器。
但是,它编译时没有错误,这听起来很奇怪,因为最后一个数组元素的引用成员将未初始化。实际上,它具有NULL值:
(gdb) p td[2].constNum
$2 = (const int &) @0x0: <error reading variable>
现在是“魔术”:我将Position_pod更改为Position
struct text_descriptor {
int id;
Position_pod pos;
const int &constNum;
};
变成这个:
struct text_descriptor {
int id;
Position pos;
const int &constNum;
};
现在它给出了预期的错误:
error: uninitialized const member ‘text_descriptor::constNum'
我的问题:为什么在第一种情况下它应该给出错误时才进行编译(与第二种情况一样)。
不同之处在于Position_pod使用C-样式花括号初始化,而Position使用C++ 11-样式初始化,它们调用Position的构造函数。但是,这如何影响未引用成员初始化的可能性?
(更新)
编译器:
gcc(Ubuntu 4.8.2-19ubuntu1)4.8.2
最佳答案
显然,
是列表初始化,并且初始化列表不为空。
C++ 11说([dcl.init.list] p3):
[dcl.init.aggr] p1:
td
是一个数组,因此它是一个聚合,因此将执行聚合初始化。
[dcl.init.aggr] p7:
在这种情况下,因此td[2]
是从一个空的初始化程序列表中初始化的,(再次[dcl.init.list] p3)表示它是值初始化的。
值初始化又意味着([dcl.init] p7):
您的类text_descriptor
是一个没有用户提供的构造函数的类,因此td[2]
首先被零初始化,然后调用其构造函数。
零初始化的方式([dcl.init] p5):
无论text_descriptor
的默认构造函数如何,这都是定义明确的:它只是将非引用成员和子成员初始化为零。
如果非平凡的构造函数被调用,则默认构造函数被调用。这是定义默认构造函数([special] p5)的方法:
因此,按预期方式删除了隐式定义的构造函数,但是如果pos
是POD类型(!),它也很简单。因为构造函数很简单,所以不调用它。因为没有调用构造函数,所以删除它的事实不是问题。
这是C++ 11中的一个漏洞,此漏洞已得到修复。碰巧已经修复了inaccessible trivial default constructors,但是措辞也涵盖了删除的琐碎默认构造函数。 N4140(大致为C++ 14)在[dcl.init.aggr] p7(强调我的)中说:
作为T.C.注释中指出,another DR也进行了更改,因此td[2]
仍然是从空的初始化程序列表中初始化的,但是该空的初始化程序列表现在意味着聚合初始化。这又意味着每个td[2]
的成员也都从一个空的初始化列表中初始化(再次[dcl.init.aggr] p7),因此似乎从{}
初始化了引用成员。
然后[dcl.init.aggr] p9说(如remyabel在现已删除的答案中指出的那样):
我不清楚这是否适用于从隐式{}
初始化的引用,但是编译器确实将其解释为相同的含义,并且含义不多。
关于c++ - C++ 11奇怪的括号初始化行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28408911/