Yes, not everyone is a fan of uninitialized data. It can be less reproducible, and so can create confusing bugs. Though as you've stated, the random data can also reveal bugs, that a default initialized value would have hidden. On the plus side, the compiler is often able to warn about use of uninitialized data. It depends a bit on the complexity of the code flow though. Not all things can be warned about, though most of the common cases will be.
In the end, it's more about efficiency, and not doing work that's just going to be overwritten later by a late initialization. It might be the proper value is not known until other work is done first.
As for the
{} initialization syntax, it will zero initialize values in many cases.
In particular, the empty
{} can be used directly after a type in
value initialization. Value initialization states a number of rules for zero-initializing. One curious rule, is that it may first initialize to zero, and then do
default initialization. In the Notes section of default initialization, you'll find that some object may be default initialized to indeterminate values. That actually means they won't be initialized. An example is an
int local variable (but not a global, static, or thread-local variable, which is zero initialized). Hence the clause about zero-initializing first in value initialization is really about zero-initializing values that would otherwise be uninitialized. So basically, the value initialization syntax will set integers to 0 and floating point values to 0.0.
In terms of the example above (note the equal sign), we actually have
aggregate initialization. Aggregate initialization has a rule where if the initializer list is shorter than the number of members to initialize, it will use value initialization on the remaining members. Hence you also get the zero-initialization here.
Example:
Rect r = {1}; // r == {1, 0, 0, 0}