23

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?

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
user3561614
  • 1,024
  • 1
  • 12
  • 20

3 Answers3

25

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

The Standardese, for those so inclined

(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(), where T 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: if T 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 type T 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?

Community
  • 1
  • 1
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Would `std::list foo()` have been a constructor call? – Paolo M Oct 12 '15 at 11:10
  • 1
    @PaoloM Looks more like a function to me. Google "Most vexing parse" – deviantfan Oct 12 '15 at 11:11
  • 6
    While correct, I don't think using the full technical terms is helpful for a beginner. For example, what's a prvalue? – Jonathan Wakely Oct 12 '15 at 11:14
  • 4
    It is commonly known as the most vexing parse, but not accurately so. – Lightness Races in Orbit Oct 12 '15 at 11:14
  • @LightnessRacesinOrbit that's why I said "commonly". Do you think there's a better way of putting it, or was your comment so that OP doesn't think it's a formal term? – TartanLlama Oct 12 '15 at 11:24
  • 3
    @TartanLlama: I mean, that _isn't_ the Most Vexing Parse, but it's often mistaken as such. – Lightness Races in Orbit Oct 12 '15 at 11:28
  • @LightnessRacesinOrbit Ah, you mean `A foo();` vs. `A foo(B());`. I think describing the former as MVP is helpful even if it's not entirely correct. Or maybe we should refer to that as the Slightly Vexing Parse :p – TartanLlama Oct 12 '15 at 11:34
  • @TartanLlama: Exactly :) – Lightness Races in Orbit Oct 12 '15 at 11:39
  • "Neither of these are constructor calls" is not true. Even if the type of statement may be called differently in the C++ standard, `return std::list();` *does* invoke the list's default constructor (if we don't consider (N)RVO). – TheOperator Oct 12 '15 at 11:40
  • @TheOperator it is true. Those syntactic forms are not calls to the constructor, they call the constructor as part of their function, as I stated in the answer. Constructors do not have names ([class.ctor]/1) so you can't explicitly call them. – TartanLlama Oct 12 '15 at 11:42
  • 1
    I find it confusing to say that none of the codes are constructor calls, yet they call the constructor. How does a constructor call look then? Even `T obj(args);` is a declaration and not a constructor call, according to this reasoning. **Edit**: Seeing your edit, does that mean there are *no* constructor calls in C++? I find this even more confusing, not to say misleading. – TheOperator Oct 12 '15 at 11:51
  • 4
    @TheOperator: Execution of the "codes" results in an invocation of the constructor _among other things_. The programmer has not directly called the constructor. In fact, it is impossible to do so. I don't see how that's "confusing" or "misleading" in the slightest. It's like... you don't say "brb, I'm going to partake in the steady increase of greenhouse gases by encouraging the production of beef".... you say "brb eating a burger". It's semantics. – Lightness Races in Orbit Oct 12 '15 at 11:53
  • Thank you both for the clarification, now I see what you mean. Maybe this should be edited to the answer, to clarify that constructors have no names and can't be explicitly called. Currently, it's not clear why the so often-used term "constructor call" is inaccurate. – TheOperator Oct 12 '15 at 11:55
  • @TheOperator a constructor call doesn't look like anything. C++ has no syntactic constructs to explicitly call a constructor. `T obj(args);` is a variable definition which will cause allocation for the `T` object (maybe on the stack, maybe in a register, maybe optimized out), map the symbol `obj` to that object, call the necessary `T` constructor, etc. I've added a bit of clarification to the answer, is that better? If yes then we can clear up this comments section. – TartanLlama Oct 12 '15 at 11:55
  • 2
    *there's so syntactic construct to explicitly and singularly call a constructor in C++* What about placement new? – Lingxi Oct 12 '15 at 12:02
  • @Lingxi With placement new you are writing a `new` expression with particular syntax which will result in the construction of an object at that place in memory provided `operator new` isn't overloaded for that type with those arguments. I could overload `void* operator new(std::size_t, void*)` for `T` to order pizza as well as constructing my instance (I think). – TartanLlama Oct 12 '15 at 12:10
  • 4
    @Lingxi: To "call a constructor" is generally understood as a direct function invocation, with the type name given as the function name (usually functional cast notation / temporary object creation misunderstood). No syntax exists to perform that. The only way you can instruct your program to invoke a constructor is as part of other, more complex semantics; placement new is one example of those complex operations. It may seem like pedantry to some, but such rigour is in fact necessary to properly understand the language's construction and, by extension, a program's correctness. – Lightness Races in Orbit Oct 12 '15 at 12:18
  • @TartanLlama Yes, it's better, thanks for the edit. I think we should leave the comment section, as it can be valuable for interested users to learn about the backgrounds (the answer itself is still not very verbose about this topic). – TheOperator Oct 12 '15 at 12:30
  • I don't understand what useful point you guys are trying to make regarding this not being a constructor call, but the first one is certainly a constructor call and certainly isn't converting anything, so I'd definitely say it's a constructor call. -1 – user541686 Oct 12 '15 at 23:27
  • @Mehrdad The standard says that is an explicit type conversion using functional notation, I can happily spout the standardise if you like. This results in the calling of the default constructor as well as the allocation of a temporary object. It is not a direct constructor call, even if it might look like one. – TartanLlama Oct 13 '15 at 06:08
  • @TartanLlama: Just because the standard calls it a llama that doesn't mean calling it a llama in your answer makes your answer better. – user541686 Oct 13 '15 at 06:19
11

look at it this way:
1) you need to create an object
2) you need to return it.

let's say the compiler looks at the expression return Foo; , the compiler thinks "hey! he wants me to return a type! a type is not a thing that I can return! I need a true variable here!"

so you can write something like

Foo temp;
return temp;

or make it shorter - call the default constructor of Foo , then return the Anonymous object I just created. you treat the constructor as a function that produces an object.

does the code return createDefaultFoo(); looks much more reasonable? well, this is what Foo() does, it creates and returns anonymous Foo obejct

in this line :

std::list<int> foo;

the compiler can tell you want an object named foo from the type std::list<int>. so the () are redundand. as answered here, adding the () will make the compiler think you declare a function.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • The `()` are not redundant. Parsing as a function call becomes a problem, but once you get past that, there is a key difference. Without `()`, it is *default-initialization*, with them it is *value-initialization*. For `std::list` both have the same effect, but that is not true for all types. – Ben Voigt Oct 12 '15 at 17:11
8

Both statements call default constructor.

return std::list<int>();

This is same as:

std::list<int> value;
return value;

Here an object is created (using default constructor) and object is returned.

std::list<int> foo;

Here object foo is created using the default constructor.

Here are other way to do the same in C++11:

std::list<int> foo;
std::list<int> foo1{}; // C++11
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Nick
  • 9,962
  • 4
  • 42
  • 80