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

前端 未结 5 2071
星月不相逢
星月不相逢 2020-12-16 11:05

The question is simple. Why does this compile:

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

And this compile:

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


        
相关标签:
5条回答
  • 2020-12-16 11:36

    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.

    0 讨论(0)
  • 2020-12-16 11:47

    because "=" is defined as a function that takes two arguments and - after performing its job - it returns the value of the first. The constructor does not return a value.

    0 讨论(0)
  • 2020-12-16 11:53

    It's language grammar restriction. The bit in parentheses in an if statement can either be an expression or it can be a restricted form of declaration which must have one of the forms:

    attribute-specifier-seq OPT decl-specifier-seq declarator = initializer-clause

    attribute-specifier-seq OPT decl-specifier-seq declarator braced-init-list

    No other forms of declaration are allowed. Note that there is no assignment going on here, only copy-initialization.

    If you want to direct-initialize an object in a select statement condition you have you use the new form of a braced-init-list (since C++11):

    if (Type var { init })
    {
        // ...
    }
    
    0 讨论(0)
  • 2020-12-16 11:59

    Here's the solution to your problem, although it doesn't quite answer the question:

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

    It's not doing what you think it's doing - bool(true) does not call the constructor in this case, it's performing a cast. For instance:

    return foo(0);
    

    is the same as:

    return static_cast<foo>(0); // or (foo)0
    

    Test:

    struct foo {
      foo(int x) {
        std::cout << "ctor\n";
      }
      foo(const foo& x) {
        std::cout << "copy ctor\n";
      }
      operator bool() {
        return true;
      }
    
    };
    
    int main(int, char**) {
      if (foo x = foo(1)) { /* */ }
    }
    

    prints "ctor". Does not call copy constructor due to copy elision.

    0 讨论(0)
  • 2020-12-16 12:03

    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).

    0 讨论(0)
提交回复
热议问题