Нулевая инициализация вложенной структуры - ошибка компилятора? - PullRequest
4 голосов
/ 05 мая 2020

Рассмотрим этот пример:

#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 думает, что с фигурными скобками должна выполняться инициализация нуля.

1 Ответ

5 голосов
/ 05 мая 2020

Во-первых: объекты b.a.a, b.a.b, b.a.c, b.a.d гарантированно инициализированы нулем. Что для float инициализируется, как если бы = 0; (не обязательно представление всех нулевых битов).

B b{}; переводится в нулевую инициализацию только в некоторых случаях (страница cppreference немного вводит в заблуждение ).

В C ++ 14: Поскольку B является агрегатом , это агрегатная инициализация, каждый член инициализируется как будто пустым списком. Итак, A a; инициализируется, как если бы A a{};. A также является агрегатом, поэтому каждый из его элементов инициализируется как будто пустым списком, который для встроенных типов имеет нулевую инициализацию.

В C ++ 11 формулировка была другой (список инициализация агрегированного класса из пустого списка фактически не считалась агрегатной инициализацией), но результат был таким же.

В C ++ 03, B b{}; была синтаксическая ошибка, но B b = {}; было разрешено, а также, оказывается, инициализировать нулем рассматриваемые числа с плавающей запятой.

В C ++ 98 правила были другими, и, короче говоря, B b = {}; будет вызывать конструктор по умолчанию A, который оставляет значения неинициализированными. Нам нравится притворяться, что инициализации C ++ 98 никогда не существовало, но некоторые компиляторы придерживались этих правил даже в 2010-е годы.


* 1030 инициализированное число с плавающей запятой гарантированно действует как false для оператора ||, см. Сравнение числа с плавающей запятой с нулем .

Стандарт гласит: «Нулевое значение, значение нулевого указателя или значение нулевого указателя на член преобразуется в false». Что не на 100% точно, но ИМО инициализированный нулем float должен считаться для этой цели «нулевым значением».

...