Is a float member guaranteed to be zero initialized with {} syntax?

In C++17, consider a case where S is a struct with a deleted default constructor and a float member, when S is initialized with empty braces, is the float member guaranteed by the standard to be zero-initialized?

struct A {
  int x{};
};

struct S
{
  S() = delete;
 
  A a;
  float b;
};

int main()
{
  auto s = S{}; // Is s.b guaranteed to be zero?
}

In my opinion, cppreference.com is not clear, saying both that:

If the number of initializer clauses is less than the number of members and basesor initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default member initializers, if provided in the class definition, and otherwise (since C++14) copy-initialized from empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.

(from here), which implies that b is guaranteed to be zero

In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.

(from here)

which implies that b is not guaranteed to be zero.

There is also a discussion that seems to imply that while not guaranteed, all known compiler zero-initialize anyway:

The standard specifies that zero-initialization is not performed when the class has a user-provided or deleted default constructor, even if that default constructor is not selected by overload resolution. All known compilers performs additional zero-initialization if a non-deleted defaulted default constructor is selected.

Related to Why does aggregate initialization not work anymore since C++20 if a constructor is explicitly defaulted or deleted?

Answer

Because S is an aggregate, S{} will perform aggregate initialization. The rule in the standard about how members are initialized when there are no initializers in the list is basically what you cited:

  • If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.
  • Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).

So for b, that’s the equivalent of float b = {};. Per the rules of list initialization, we have to get all the way down to 3.10:

Otherwise, if the initializer list has no elements, the object is value-initialized.

And value initialization will initialize a float to 0.