jms
jms

Reputation: 769

Questions about struct initialization syntax intricacies

I have gradually accumulated some open questions about the language's initialization syntax. Searching for answers can sometimes be relatively difficult when one does not know what to search for or is not aware of the correct terminology.

Are my assumptions as expressed in the comments below correct, and what is actually happening if not? I am using C++11.

struct Foo{};
Foo;       //1:  Error, this fails for all types, built-in or otherwise
Foo();     //2:  rvalue created by an explicit call to the default ctor "Foo::foo()"
Foo{};     //3:  rvalue created by specifying an empty initializer list,
           //    ..at which point is the default constructor called? How
           //    ..does this actually differ from #2?
Foo={};    //4:  Error, why does this syntax not work?
Foo(){};   //5:  Error, this one is quite obvious considering #2
Foo()={};  //6:  what exactly happens here?
Foo f;     //7:  instance created by implicit call to default constructor
Foo f{};   //8:  instance created by initializer list, similar to #3
Foo f={};  //9:  similar to #8
Foo f();   //10: function declaration.
Foo f()={};//11: Error, this is a malformed function declaration
Foo f(){}  //12: function definition

Upvotes: 0

Views: 154

Answers (2)

Brian Bi
Brian Bi

Reputation: 119089

Initialization is a confusing topic because the various forms of initialization (direct-initialization, list-initialization, value-initialization, etc.) are not all mutually exclusive.

I went through the C++11 standard and read all the gory details, if you're curious. Here's a line-by-line analysis.

struct Foo{};

Since Foo is a class type, default-initialization of Foo calls the default constructor (8.5 [dcl.init] paragraph 6).

Since Foo is a non-union class type without a user-provided constructor, value-initialization of Foo entails zero-initialization followed by calling the implicitly-declared default constructor, if it is non-trivial. (8.5 [dcl.init] paragraph 7).

In this case, the implicitly-declared default constructor is trivial (12.1 [class.ctor], paragraph 5) and is equivalent to Foo::Foo() {} (Id., paragraph 6).

Foo is an aggregate and may be initialized by an initializer list. (8.5.1 [dcl.init.aggr], paragraph 1)

Foo;

Ill-formed. However, the declaration Foo Foo; is valid and declares a variable named Foo of type Foo. The expression Foo; would then become valid. Needless to say, you shouldn't do this.

Foo();

This is an explicit type conversion. Since Foo is a non-array complete object type, this expression creates a prvalue of type Foo, which is value-initialized. (5.2.3 [expr.type.conv] paragraph 2).

Foo{};

This is also an explicit type conversion, which creates a prvalue of type Foo which is direct-list-initialized (Id., paragraph 3). Since the braced-init-list is empty and Foo is a class type with a default constructor, the object is value-initialized (8.5.4 [dcl.init.list], paragraph 3).

Foo={};

Well, this doesn't make any sense. What did you expect it to do? (Note: Again, this becomes well-defined if Foo is a variable.)

Foo(){};

I agree, it's pretty obvious that this is ill-formed, at least as an expression. It is, however, valid syntax for the definition of the default constructor of Foo. (The extraneous semicolon is an empty declaration.)

Foo()={};

This creates the prvalue Foo() as previously detailed, then performs an assignment to it. This is allowed even though Foo() is an rvalue (13.5 [over.oper], paragraph 7). This expression on the whole is equivalent to Foo().operator=({}) (5.17 [expr.ass], paragraph 9). It constructs a temporary Foo by aggregate-initialization from the braced-init-list {}, binds the temporary Foo to an rvalue reference, Foo&&, and calls Foo's implicitly defined move assignment operator (12.8 [class.copy], paragraphs 20-21).

Foo f;

This invokes default initialization (8.5 [dcl.init], paragraph 11).

Foo f{};

This is list-initialization, since the initializer is a braced-init-list (8.5 [dcl.init], paragraph 16). Specifically it is direct-list-initialization (8.5.4 [dcl.init.list], paragraph 1). As in the case of Foo{}, value-initialization is performed.

Foo f={};

This is copy-initialization (8.5 [dcl.init], paragraph 14). Again, since the initializer is a braced-init-list, it is list-initialization; specifically, copy-list-initialization (8.5.4 [dcl.init.list], paragraph 1). Again, value-initialization is performed.

Foo f();

This is indeed a function declaration, since a statement which could be interpreted as either a function declaration or an object declaration is always interpreted as the former (8.2 [dcl.ambig.res], paragraph 1).

Foo f()={};

This can be interpreted as neither a valid object declaration nor a valid function declaration, so it is ill-formed.

Foo f(){}

Indeed, this is a function definition.

Upvotes: 1

jms
jms

Reputation: 769

After putting some thought into it, I arrived at the following conclusion:

  • Foo f; is a default initialization.
  • Foo();, Foo{}; and Foo f{}; are value initializations.
  • Foo f={}; is an aggregate initialization.
  • Foo()={}; and Foo{}={}; are actually multiple operations: If I'm correct, a value initialization of Foo(), followed by a second value initialization of {} (the type is deduced by the compiler), and finally ending with an assignment operation (by the default assignment operator).
  • Foo f(); is treated as a function declaration
  • Foo f(){} is a function definition

Upvotes: 0

Related Questions