Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using parentheses with a default constructor result in creation of the variable? [duplicate]

After watching Louis Brandy talk at CppCon 2017 I was shocked to discover that this code actually compiles:

#include <string>

int main() {

    std::string(foo);

    return 0;
}    

And for some reason std::string(foo) it is identical to std::string foo i.e. declaring a variable. I find it absolutely counterintuitive and can't see any reason for C++ to work that way. I would expect this to give an error about undefined identifier foo.

It actually makes expressions like token1(token2) have even more possible interpretations than I previously thought.

So my question is: what is the reason for this horror? When is this rule actually necessary?

P.S. Sorry for the poorly worded title, please, feel free to change it!

like image 478
Amomum Avatar asked Dec 29 '17 21:12

Amomum


People also ask

Does default constructor initialize members?

Default constructors are one of the special member functions. If no constructors are declared in a class, the compiler provides an implicit inline default constructor. If you rely on an implicit default constructor, be sure to initialize members in the class definition, as shown in the previous example.

Is default constructor mandatory in c++?

The compiler-defined default constructor is required to do certain initialization of class internals. It will not touch the data members or plain old data types (aggregates like an array, structures, etc…). However, the compiler generates code for the default constructor based on the situation.

Can constructor be protected in c++?

A protected constructor can be used to make a class effectively abstract when none of its methods are pure-virtual. It is not quite abstract in the C++ sense since friend classes can still use it without overriding, but then you would have to declare these.


1 Answers

Since this question is tagged language-lawyer, the direct answer is that, from [stmt.ambig]:

There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.

And, similarly, for functions, in [dcl.ambig.res]:

The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in [stmt.ambig] can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in [stmt.ambig], the resolution is to consider any construct that could possibly be a declaration a declaration.

Hence:

Why oh why is std::string("foo") so different from std::string(foo)

The former cannot be a declaration. The latter can be a declaration, with a redundant set of parentheses. Thus, the former isn't a declaration and the latter is.

The underlying issue is that, grammaticaly, declarators can start with a ( which could make it indistinguishable from a function-style explicit type conversion. Rather than come up with arbitrary complex rules to try to determine what the user meant, the language just picks one, and it's easy enough for the user to fix the code to actually do what he meant.

like image 135
Barry Avatar answered Nov 15 '22 06:11

Barry