以下是struct
的简明定义,仅显示问题点。
struct Entry {
// bookkeeping record for managing solution search
std::array<std::array<bool, DIM>, DIM> filled; // which holes have been filled
std::array<std::array<char, MAX>, MAX> cells; // individual cell entries, 0=empty
std::vector<Constraint> overlaps;
std::vector<Hole*>::iterator candidates;
Entry() = default;
};
这实际上是一个错误。我当时认为默认构造函数会对数组进行零初始化,但它只是用随机垃圾填充它们。我现在知道我需要编写默认构造函数,但是我对测试时遇到的行为感到困惑。
这是我的测试功能的简化版本:
void testEntry(void) {
Entry e;
std::cout << std::boolalpha;
e.cells[1][2] = 'a';
e.filled[0][0] = true;
for (int i = 0; i < MAX; ++i)
for (int j = 0; j < MAX; ++j)
std::cout<<i<<" "<<j<<" "<<e.cells[i][j]<<std::endl;
for (int i = 0; i < DIM; ++i)
for (int j = 0; j < DIM; ++j)
std::cout<<i<<" "<<j<<" "<<e.filled[i][j]<<std::endl;
}
当我运行它时,filled
和cells
数组包含随机垃圾,最终我发现了我的错误。在调试时,我将声明Entry e;
更改为Entry e{};
,修改后的代码似乎按照我的意图运行,但我不明白为什么。
我无法完全遵循文档。在list initialization下,它表示,“如果braced-init-list为空且T是具有默认构造函数的类类型,则执行值初始化。”这很清楚,但是当我去value initialization时,我无法弄清楚哪些情况适用(如果有的话)。 似乎正在应用zero-intialization下的此条款 “如果T是非联合类类型,则所有基类和非静态数据成员都是零初始化的,并且所有填充都初始化为零位。构造函数(如果有的话)被忽略,”但我看不到怎么到这儿。
最重要的是,我想知道这个行为是否实际在规范中定义,或者它是否与不同的编译器不同。 (我在Xcode 7.2.1下使用clang)。
答案 0 :(得分:6)
结构声明中空括号的含义是什么?
T object {};
是__atomic
的语法。没有大括号,对象将是value initialization (4)。
要迂腐,这不是&#34;结构声明&#34;。它是变量的声明。
当我进行值初始化时,我无法确定哪些情况适用(如果有的话)。
这适用:
2)如果T是具有默认构造函数的类类型,该构造函数既不是用户提供也不是删除(也就是说,它可能是具有隐式定义或默认默认构造函数的类),该对象是零初始化的并且如果它有一个非平凡的默认构造函数,那么它是默认初始化的; (自C ++ 11起)
这就是我们如何实现零初始化。您对零初始化规则的解释是正确的。
我想知道这种行为是否实际上是在规范中定义的
链接页面中的规则基于标准,而不是基于特定实现。行为已定义。
答案 1 :(得分:1)
标准中的措辞在C ++ 11和C ++ 14之间发生了变化,最终结果相似但不完全相同。
此代码是 list-initialization ,因为支持列表(尽管为空)是初始化程序。在C ++ 11中,[dcl.init.list] / 3的以下引用适用:
对象或类型T的引用的列表初始化定义如下:
- 如果初始化列表没有元素且T是具有默认构造函数的类类型,则该对象是值初始化的。
然而,在C ++ 14中,同一部分以不同的要点开头:
对象或类型T的引用的列表初始化定义如下:
- 如果T是聚合,则执行聚合初始化
在这种情况下,Entry
是一个聚合,因为它符合一个条件([dcl.init.aggr] / 1):
聚合是一个数组或没有用户提供的构造函数的类(12.1),没有私有或受保护的非静态数据成员,没有基类,也没有虚函数
“用户提供”一词不包括默认功能。
聚合初始化的行为在[dcl.init.aggr]中有所说明,它表示列表的连续成员被视为聚合成员的初始化者。当列表成员少于聚合成员时,[dcl.init.aggr] / 7适用:
如果列表中的initializer-clause少于聚合中的成员,那么未明确初始化的每个成员都应从其 brace-or-equal-initializer 初始化,如果有的话是没有支撑或平等 - 初始值设定项,来自空的初始化列表。
这就是说,例如,e.overlaps
的初始化方式与std::vector<Constraint> overlaps{};
相同。
您可能会注意到这是聚合的递归定义。当我们得到一个不是聚合的东西(例如std::vector
)时,[dcl.init.list] / 3中的第二个项目符号将被应用,这是C ++ 11中的第一个项目:值 - 初始化。
比较C ++ 11和C ++ 14,有一点不同:在C ++ 14中,在调用默认构造函数之前,没有零初始化整个对象的步骤成员。相反,只有没有用户提供的构造函数的子对象才会被初始化为零。在C ++ 14中,Entry
中的struct padding可能未初始化为零位。 (std::array
成员之后可能有填充)。同样,如果std::vector
的内部包含任何未由其构造函数初始化的成员;它们将保持未初始化状态,而不是零初始化。