0

There are quite a few posts on SO with similar titles, but they seem to be triggered by various syntactic errors and I didn't see a consistent pattern yet..

using namespace std;

class A
{
public:
    A(int a_) : a(a_) {}
    int a;
};

int main()
{
    A x{3};
    A y{0};

    if ((y=x).a)
        cout << y.a << endl;
        
    int i = 1;
    if (int j = i)
        cout << j << endl;
    
    if ((A z = x).a) // error: expected primary-expression before ‘z’
        cout << z.a << endl;

    (int m = 1); // error: expected primary-expression before ‘int’
}

Am I wrong to assume A z = x is an assignment expression, which should have the same value as z?

QnA
  • 1,035
  • 10
  • 25
  • 1
    tested more and it seems that the problem is not the `if` keyword, but that I can not wrap a declaration inside a parenthesis. – QnA May 30 '21 at 00:03
  • See also [c++ - Can I write this if statement with a variable declaration on one line? - Stack Overflow](https://stackoverflow.com/questions/45999057/can-i-write-this-if-statement-with-a-variable-declaration-on-one-line) – user202729 Dec 13 '21 at 10:56

2 Answers2

5

Am I wrong to assume A z = x is an assignment expression

Yes, you are wrong. There is no assignment going on here. The = in this statement represents initialization, not assignment. The statement A z = x; defines the variable z, where z is constructed from x. The copy constructor is used here, not copy assignment. It is a declaration statement, not an expression statement.

Your confusion is reasonably common, and it is made worse by the fact that the condition in an if statement can be a declaration of a single non-array variable with a brace-or-equals initializer. Syntactically, a declaration with an "equals" initializer can look a lot like an assignment. A big difference, as you discovered, is that you cannot treat the declaration as a sub-expression. The condition is either a declaration or an expression, not a mix of both.

The good news is that C++-17 added an optional init-statement to the if statement syntax. So what you appear to want would be achieved by the following.

    if ( A z = x; z.a )  // Semicolon separates init-statement from condition
        cout << z.a << endl;
    // At the end of the `if` statement, `z` goes out of scope.
JaMiT
  • 14,422
  • 4
  • 15
  • 31
3

You can't declare a variable in an if statement in that fashion. The declaration has to be of the form:

if (X x = y) ... (or if (auto x = y) ...)

However, you can still achieve what you are trying to do if you provide a suitable conversion operator in class A, like this:

#include <iostream>

using namespace std;

class A
{
public:
    A(int a_) : a(a_) {}
    int a;
    operator bool () { return a != 0; }
};

int main()
{
    A x{3};
    const A &y = x;
    if (y.a)
        cout << y.a << endl;
    if (A z = x)
        cout << z.a << endl;
}

Live demo

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • thanks! In reality the `A` is a `std::map`, and the `.a` is `.size()`, and I don't want to derive from `map` just to add a `bool ()` for `.size()`. I guess the workaround would be to define a temporary variable? Like `auto & val = func(); if (val.size()) blahblah;` ? – QnA May 30 '21 at 00:01
  • Yes, that's what I would do. It is UB to derive from `std::map`. – Paul Sanders May 30 '21 at 00:02
  • UB? There is no UB in deriving standard library classes. – StoryTeller - Unslander Monica Aug 28 '21 at 19:44
  • @StoryTeller-UnslanderMonica OK, I stand corrected. But I thought at least it was discouraged. – Paul Sanders Aug 28 '21 at 20:02
  • It's bad when polymorphic deletion has to happen, because it can't. But that's the extent of it, and it's the same as for any class. Most uses of standard containers don't involve that at all. Value semantics and pass by references is used mostly. – StoryTeller - Unslander Monica Aug 28 '21 at 20:05