I'm a C++ beginner and would like to understand why
return std::list<int>();
needs parentheses, but
std::list<int> foo;
doesn't need parentheses. What's the difference between these constructor calls?
Dr. Bjarne: No. temporary objects of the class types are useful.
A constructor is a member function of a class that is used to create objects of that class. It has the same name as the class itself, has no return type, and is invoked using the new operator. An ordinary member function has its own name, a return type (which may be void), and is invoked using the dot operator.
in simple words a class is like a blueprint and defines the framework that other objects can inherit, a constructor is something that actually creates the object in the program whereas the class only gives the guidelines.
Neither of these are constructor calls.
The first is an explicit type conversion, which creates an object of type std::list<int>
.
The second is a variable definition which creates an object of type std::list<int>
.
The default-constructor (constructor taking no arguments) is called as part of the creation in both cases.
Although you might see such things talked about as "constructor calls", there's no syntactic construct to explicitly and singularly call a constructor in C++.
The reason one needs parentheses when the other doesn't is because they are two separate language constructs with different syntax rather than two ways to call a constructor.
Note that if you add parentheses to your second example, you actually declare a function rather than defining a variable:
std::list<int> foo; //variable definition std::list<int> foo(); //function taking no args, returning a std::list<int>
This is commonly known as the most-vexing-parse. C++11 introduced braced-initialization to get around this:
std::list<int> foo{}; //variable definition
(Quotes from N3337)
"But T()
sure looks like a constructor call, why is it not?"
In that context, T()
is known as an explicit type conversion with functional notation:
5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
1 [...]
2 The expression
T()
, whereT
is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, which is value-initialized (8.5; no initialization is done for the void() case). [Note: ifT
is a non-class type that is cv-qualified, the cv-qualifiers are ignored when determining the type of the resulting prvalue (3.10). —end note ]
So this creates a prvalue which is value-initialized.
[dcl.init]/7:
To value-initialize an object of typeT
means:— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— [...]
So this calls the constructor as part of the value-initialization, which is part of an explicit type conversion. As stated above, there is no way to call a constructor directly. The standard says:
[class.ctor]/1:
Constructors do not have names. A special declarator syntax is used to declare or define the constructor. The syntax uses:— an optional decl-specifier-seq in which each decl-specifier is either a function-specifier or constexpr,
— the constructor’s class name, and
— a parameter list
in that order. In such a declaration, optional parentheses around the constructor class name are ignored.
So constructors don't have names and we declare/define them with a syntax exception which the language defines.
"This seems like an academic distinction, does this matter in practice?"
Maybe, maybe not. My opinion is that interpreting syntax like the above as pure constructor calls paints an incorrect picture of what a constructor is. A constructor initializes an object; it doesn't allocate that object's memory, return the initialized object, bind a symbol to that object or anything else which is done by variable definitions and type conversions. Furthermore, it can create confusion like that of the OP, who expected uniform syntax because he thought those two constructs are both constructor calls.
Why use inexact synecdoche when we have formal terms which avoid confusion?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With