c++ - 嵌套结构的零初始化 - 编译器错误?

标签 c++ c++14

考虑这个例子:

#include <vector>
#include <stdexcept>

struct A
{
    float a;
    float b;
    float c;
    float d;
};

struct B
{
    A a;
    std::vector<int> b;
};
    

int main() {

    B b{};

    if (b.a.a || b.a.b || b.a.c || b.a.d) throw std::runtime_error("Compiler bug?");
}

如果我没理解错的话,根据https://en.cppreference.com/w/cpp/language/zero_initialization ,它不能抛出,因为应该为 B::a 执行零初始化,因为它应该为“没有构造函数的值初始化类类型的成员”。

如果它抛出,是编译器错误还是我遗漏了什么?

[编辑]

这里使用 clang 10 并启用优化,它只执行“mov eax, 2”和“ret”(意味着条件为假): https://godbolt.org/z/CXrc3G

但是如果我去掉大括号,它会执行“mov eax, 1”和“ret”(意味着条件为真)。但在这里我认为它可以返回任何它想要的东西,因为它只是 UB。 https://godbolt.org/z/tBvLzZ

所以 clang 似乎认为必须执行大括号零初始化。

编辑:我在英特尔网站上提交了一个错误: https://community.intel.com/t5/Intel-C-Compiler/Aggregate-initialization-bug-with-nested-struct/td-p/1178228

一位情报人员回答说“我已经向我们的开发人员报告了这个问题。”可怜的开发者,一手支撑所有icc开发。

最佳答案

首先:对象 b.a.ab.a.bb.a.cb.a.d 保证被零初始化。 float 的初始化就好像 = 0; (不一定是所有位都为零的表示)。

B b{}; 在某些情况下仅转换为零初始化(cppreference 页面有点误导)。

在 C++14 中:由于 B 是一个 aggregate ,这是聚合初始化,每个成员都被初始化为一个空列表。所以 A a; 就像 A a{}; 一样被初始化。 A 也是一个聚合,因此它的每个元素都被初始化为一个空列表,对于内置类型来说是零初始化。

在 C++11 中,措辞不同(从空列表对聚合类的列表初始化实际上不被视为聚合初始化)但结果是相同的。

在 C++03 中,B b{};是一个语法错误,但是 B b = {}; 是允许的,而且结果是对有问题的 float 进行了零初始化。

在 C++98 中,规则不同,长话短说,B b = {}; 会调用 A 的默认构造函数未初始化的值。我们喜欢假装 C++98 初始化从未存在过,但一些编译器甚至在 2010 年代仍坚持这些规则。


除此之外,对于 || 运算符,零初始化 float 是否保证充当 false 可能存在一些争论,请参阅 Comparing floating point number to zero .

标准说“零值、空指针值或空成员指针值被转换为 false”。这不是 100% 精确,但 IMO 零初始化的 float 应该算作此目的的“零值”。

关于c++ - 嵌套结构的零初始化 - 编译器错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61603820/

相关文章:

c++ - 算法复杂度 - O(log n) 的解释?

C++使用lambda进行隐式构造函数调用,期望函数指针

c++ - 有没有办法强制实例只在堆栈上?

c++ - 对已定义方法的 undefined reference , header 中的声明与源匹配

c++ - 错误: 'operator=' 不匹配(操作数类型为 'std::map<int, double>::iterator

c++ - 双向链表 std::unique_ptr 类在删除节点时无法按预期工作

c++ - 有模板复制构造函数时不生成默认构造函数

c++ - 允许模拟类继承自最终类

c++ - 不使用构造函数初始化priority_queue

c++ - GCC 中树莓派的交叉编译。从哪儿开始?