Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't variables defined in a conditional be constructed with arguments?

Tags:

The question is simple. Why does this compile:

bool b(true); if (b) { /* */ } 

And this compile:

if (bool b = true) { /* */ } 

But not this:

if (bool b(true)) { /* */ } 

In my real code, I need to construct an object and test it, while also having it destroyed when the if-block ends. Basically, I'm looking for something like this:

{     Dingus dingus(another_dingus);     if (dingus) {         // ...     } } 

Of course, this would work:

if (Dingus dingus = another_dingus) { /* */ } 

But then I'm constructing a Dingus and calling operator= on it. It seems logical to me that I would be able to construct the object using whatever constructor I please.

But I'm baffled why this isn't grammatically correct. I've tested with G++ and MSVC++ and they both complain about this construct, so I'm sure it's part of the spec but I'm curious as to the reasoning for this and what non-ugly workarounds there may be.

like image 761
cdhowie Avatar asked Nov 30 '11 20:11

cdhowie


People also ask

Can you declare a variable in an if statement C++?

If you're new to the syntax that's used in the code sample, if (int i = 5) { is a perfectly valid way of declaring and defining a variable, then using it inside the given if statement.

Can you initialize variables in a switch statement?

The variable can be declared, but it cannot be initialized.

Can you declare a variable in an if statement Java?

Java allows you to declare variables within the body of a while or if statement, but it's important to remember the following: A variable is available only from its declaration down to the end of the braces in which it is declared. This region of the program text where the variable is valid is called its scope .

How do you put variables in a switch case?

You can still declare variables in switch statements, you just have to put curly brackets around the code after the case label.


2 Answers

It's a bit technical. There's no reason why what you want couldn't be allowed, it just isn't. It's the grammar.

An if statement is a selection statement, and it takes the grammatical form:

if (condition) statement 

Here, condition can be either:

  • expression or
  • type-specifier-seq declarator = assignment-expression

And there you have it. Allowing a declaration in a condition is a special case, and it must follow that form or your program is ill-formed. They could have probably allow direct-initialization instead of copy-initialization, but there isn't really any motivation to do so now. As Johannes Schaub points out, this change would break existing code, so it's pretty much never going to happen.

Let_Me_Be notes that C++11 added a third form (I'm ignoring attributes here):

decl-specifier-seq declarator braced-init-list 

So if (bool b{true}) is fine. (This can't possibly break any valid existing code.)


Note your question seems to do with efficiency: don't worry. The compiler will elide the temporary value and just construct the left-hand side directly. This, however, requires your type be copyable (or movable in C++11).

like image 115
GManNickG Avatar answered Sep 25 '22 16:09

GManNickG


It should be noted.that if(functor(f)(123)) ...; would then not be the call of an anonymous functor with argument 123 anymore but would declare a functor initialized by 123.

And i think introducing such pitfalls for that little feature is not worth it.


Since it may not be clear what the above means, let's take a deeper look. First remember that parentheses around a declarator are allowed, including for the degenerate case of being directly around a declared name:

int(n) = 0;  // same: int n = 0;   int(n)(0); // same: int n(0); 

Both of the parenthesized versions are ambiguous, because the first could be an assignment and the second could be a function call. But both could also be declarations. And the Standard says that they are declarations.

If we will allow paren-initializers in conditions, then we introduce the latter ambiguity into conditions too, just as for the statement case. Thus we would make valid condition expressions that are used nowadays into declarations after the feature is supported. Consider

typedef bool(*handler_type)(int);  bool f(int) { /* ... */ } bool f(int, int) { /* ... */ }  void call_it() {    // user wants to call f(int), but it is overloaded!    // -> user tries a cast...    if(handler_type(f)(0)) {      /* ... */    } } 

What you think will happen? Of course, it will never enter the if body, because it always declares a null pointer. It never calls function f. Without the "feature" it will properly call f, because we don't have the ambiguity. This is not limited to only (f), but also (*f) (declares a pointer), (&f) (declares a reference) et al.

Again: Do we want such pitfalls as price for such a small feature? I don't know how many people even know they could declare stuff in a condition.

like image 42
Johannes Schaub - litb Avatar answered Sep 23 '22 16:09

Johannes Schaub - litb