The form of initialization you are using for m_str
is called brace-or-equal-initializer by the standard.
In the first paragraph under Intializers, the standard states:
8.5 Intializers
1 A declarator can specify an initial value for the identifier being declared. The identifier designates a variable being initialized. The process of initialization described in the remainder of 8.5 applies also to initializations specified by other syntactic contexts, such as the initialization of function parameters with argumet expressions (5.2.2) or the initialization of return values (6.6.3).
initializer:
brace-or-equal-initializer
( expression-list )
brace-or-equal-initializer:
= initializer-clause
braced-init-list
And in the section on class members...
9.2 Class members
5 A member can be initialized using a brace-or-equal-initializer.
And in the section on initializing bases and members...
12.6.2 Initializing bases and members
8 In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
— if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initializedas specified in 8.5
Coming to your class,
foo() = default;
is equivalent to:
foo(){}
When a default constructor with no member initializer list is used, the member data are default initialized. In the default initialization process, for members that have a brace-or-equal-initializer, that form is used to initialize them. In your case,
foo(){}
is equivalent to:
foo() : m_str("Hello") {}