Why does C++ allow us to surround the variable name in parentheses when declaring a variable?
Asked Answered
S

2

91

For example a declaration such as that:

int (x) = 0;

Or even that:

int (((x))) = 0;

I stumbled upon this because in my code I happened to have a fragment similar to the following one:

struct B
{
};

struct C
{
  C (B *) {}
  void f () {};
};

int main()
{
  B *y;
  C (y);
}

Obviously I wanted to construct object C which then would do something useful in its destructor. However as it happens compiler treats C (y); as a declaration of variable y with type C and thus it prints an error about y redefinition. Interesting thing is that if I write it as C (y).f () or as something like C (static_cast<B*> (y)) it will compile as intended. The best modern workaround is to use {} in constructor call, of course.

So as I figured out after that, it's possible to declare variables like int (x) = 0; or even int (((x))) = 0; but I've never seen anyone actually using declarations like this. So I'm interested -what's the purpose of such possibility because for now I see that it only creates the case similar to the notorious "most vexing parse" and doesn't add anything useful?

Scorpaenoid answered 16/4, 2015 at 13:5 Comment(4)
possible duplicate of What is the purpose of a declaration like int (x); or int (x) = 10;Hundred
@Hundred Funny how the text of my question answers to the question from the second answer in your linked question, since I provide the example where allowing such declarations leads to unexpected results :)Scorpaenoid
#6342451Despinadespise
walked into this trap: didnt know mutex was commented out and then accidently wrote the incorrect parenthesis declaration. //std::mutex std::unique_lock<std::mutex>(m_mutex);Lid
K
88

Grouping.

As a particular example, consider that you can declare a variable of function type such as

int f(int);

Now, how would you declare a pointer to such a thing?

int *f(int);

Nope, doesn't work! This is interpreted as a function returning int*. You need to add in the parentheses to make it parse the right way:

int (*f)(int);

The same deal with arrays:

int *x[5];   // array of five int*
int (*x)[5]; // pointer to array of five int
Karl answered 16/4, 2015 at 13:11 Comment(8)
And to complete the answer: disallowing the particular case that the asker is asking about would require an extra special-case rule. The current definition of how () work in a type is uniform throughout the type.Sowers
So the special-case applies to the most-vexing parse. This is because the syntax for initializing variables with constructor arguments was added later (in a hurry I guess?).Swansdown
@FISOCPP Well. . yes. C++ came after C. . .Marmoreal
Does this answer equally apply to C, not just C++?Cosmetic
"variable of function type" what?!!Thromboembolism
@Swansdown This is why C++ parser is a much more complicated than a C one - x(y) = z could be either a variable definition with initialization or a call to a constructor that returns an object with = or a call to a function that returns a reference that is assigned to or...Wray
@Thromboembolism Functions are first-class objects... except that they're not objects.Epigraph
@AnttiHaapala Note that although x(y) = z cannot be well formed expression in C, it's still grammatically an expression if all 3 names are valid expressions (if they are either function names or variable names). It doesn't matter at that point that x(y) is always a rvalue in C.Thromboembolism
P
17

There's generally allowed to use parentheses in such declarations because the declaration, from the syntactical point of view looks always like this:

<front type> <specification>;

For example, in the following declaration:

int* p[2];

The "front type" is int (not int*) and the "specification" is * p[2].

The rule is that you can use any number of parentheses as needed in the "specification" part because they are sometimes inevitable to disambiguate. For example:

int* p[2]; // array of 2 pointers to int; same as int (*p[2]);
int (*p)[2]; // pointer to an array of 2 ints

The pointer to an array is a rare case, however the same situation you have with a pointer to function:

int (*func(int)); // declares a function returning int*
int (*func)(int); // declares a pointer to function returning int

This is the direct answer to your question. If your question is about the statement like C(y), then:

  • Put parentheses around the whole expression - (C(y)) and you'll get what you wanted
  • This statement does nothing but creating a temporary object, which ceases to living after this instruction ends (I hope this is what you intended to do).
Patentee answered 16/4, 2015 at 13:14 Comment(4)
As I mentioned, initially it was doing something in destructor, I guess it's pretty standard thing when you have some number of "chaining" functions for setting some parameters and then doing all the job in the destructor. Thanks for one more workaround though, I guess writing {} is the most valid one after all.Scorpaenoid
try to avoid making up your own grammar and use the one provided in the standard. <front-type> <specification> is misleading, and wrong. The grammar is <declaration-specifier> <declarator>Mile
You're right - I didn't look into the standard, just repeated the rule from my head. Actually in C++11 the role of <declaration-specifier> can be also played by auto keyword, so it's not even always a type.Patentee
@Pravasi Meet: If you have edited the part of my post and changed names in the given syntax schema, please change the names 3 lines below accordingly as well. Otherwise there are still old names "front type" and "specification" and therefore the post doesn't make any sense.Patentee

© 2022 - 2024 — McMap. All rights reserved.