Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logical AND + assignment in c++, safe?

I just learned this great pattern (from javascript actually) and I would like to apply it to my c++ code.

To explain the pattern, let's say I am representing a string as a linked list of these:

struct link_char;
struct link_char
{
   link_char * next;
   char code;
};

Note that the last character of any link_char string will always have code==0. This property means that I can check for a value in the string, while using && short-circuiting to prevent NULL pointer access.

bool equals_hello( const link_char * first_char )
{
    const link_char * c = first_char;

    return       c->code=='h' 
    && (c=c->next)->code=='e' 
    && (c=c->next)->code=='l' 
    && (c=c->next)->code=='l' // if string == "hel", we short-circuit here
    && (c=c->next)->code=='o';
}

My question is about safety, not readability. I know the short-circuiting will work as long as && is not overloaded. But will the assignment operations happen in the right order, or is it implementation defined?

The above example is explicit about where reads/writes can happen, but I would also like to use this pattern in situations where there can be side-effects. For example:

// think of these as a bunch of HRESULT type functions 
//   a return value of 0 means SUCCESS
//   a return value of non-zero yields an Error Message
int err;
( !(err=initialize()) && !(err=create_window()) && !(err=run_app() )
    || handle_error(err);

Will these kinds of operations work as intended cross-platform? I've read that "if you read a variable twice in an expression where you also write it, the result is undefined". But intuitively I feel like the short-circuiting guarantees the order, does it not?

like image 503
cmeub Avatar asked Jul 01 '11 05:07

cmeub


People also ask

What does && mean in C++?

The logical AND operator ( && ) returns true if both operands are true and returns false otherwise. The operands are implicitly converted to type bool before evaluation, and the result is of type bool . Logical AND has left-to-right associativity.

Is assignment a logical operator?

Therefore, the logical OR assignment operator ( ||= ) doesn't assign the string 'untitled' to the title variable. Like the logical OR operator, the logical OR assignment also short-circuits. It means that the logical OR assignment operator only performs an assignment when the x is falsy.

How does the assignment operator work?

The assignment operators return the value of the object specified by the left operand after the assignment. The resultant type is the type of the left operand. The result of an assignment expression is always an l-value. These operators have right-to-left associativity.


2 Answers

Yes.

Built-in logical AND (&&), logical OR (||) and the comma operator (,) are the only cases in which for a binary operator C++ guarantees that evaluation will compute left expression and then (if not short circuited) right expression (comma operator of course always evaluates both operands, first left and then right).

Note also that the comma between function arguments is not a comma operator and therefore the order of evaluation of function arguments is not specified and even worse than that: for example in f(g(h()),i()) it is possible that the sequence of calls will be h,i,g,f.

Also the guarantee about evaluation order only applies to built-in operators; if you redefine them then they basically become function calls where the order of evaluation of arguments is not guaranteed and where short-circuiting is not performed.

Other binary operators don't guarantee the order of evaluation and for example one common mistake is to think that in:
std::cout << foo() << bar();

the call to foo() is guaranteed to happen before the call to bar() ... this is not true.

(C++17 fixed this issue but only in a few very special cases, including the left shift operator because it's used for streams)

Of course the order of evaluation is also guaranteed for the ternary :? operator, where only one of the two other expressions will be evaluated after first evaluating the condition.

Another place in which the order of evaluation is guaranteed (and sometimes surprising for newbies) is member initialization lists for constructors, but in this case the order is not the one in the expression, but the order of member declaration in the class.... for example:

struct Foo
{
   int x, y;
   Foo() : y(compute_y()), x(compute_x()) {}
};

in this case it is guaranteed that the call compute_x() will be done BEFORE the call compute_y() because x precedes y in the member declarations.

like image 182
6502 Avatar answered Oct 08 '22 02:10

6502


Will these kinds of operations work as intended cross-platform? I've read that "if you read a variable twice in an expression where you also write it, the result is undefined". But intuitively I feel like the short-circuiting guarantees the order, does it not?

The built-in && operator has guaranteed short-circuit evaluation, which means that it introduces a sequence point: C++98 §5.14/2 "All side effects of the first expression except for destruction of temporaries (12.2) happen before the second expression is evaluated".

So there's no problem wrt. C++.

Still your suggested usage is in my opinion very ungood, since it is obscure. Simply don't use language features that you have to ask about, because others will most probably be similarly unclear about them. Also, re comments in the code, be aware that a Windows HRESULT indicates failure when bit 31 is set, which is very different from zero/non-zero.

Cheers & hth.,

like image 37
Cheers and hth. - Alf Avatar answered Oct 08 '22 02:10

Cheers and hth. - Alf