Purpose of the C++ keyword: &= (a.k.a. 'and_eq') - c++

Neither cppreference nor cplusplus, and nor Microsoft websites provided the verbose definition of the C++ keyword &= (a.k.a. and_eq), despite examples that seem to be a bit cryptic for a novice.
Yet the book C++ In a Nutshell: A Desktop Quick Reference by Lischner provided the following (p. 291, 2003):
The and_eq operator is an assignment operator that performs bitwise and.
Quoting the example from Microsoft:
#include <iostream>
#include <iso646.h>
int main( )
{
using namespace std;
int a = 3, b = 2, result;
result= a &= b;
cout << result << endl;
}
yields 2.
Accordingly, could you please tell me whether &= is a shortcut for concatenated assignments (if such a thing exists?), for example: result = a = b;?

No. a &= b is a shortcut for a = a & b.
Additionally, a = b returns the value that was assigned to a. So result = a &= b is short for:
a = a & b;
result = a;

&=, and all of the other op= built in operators are described in [expr.ass]\7 as
The behavior of an expression of the form E1 op= E2 is equivalent to E1 = E1 op E2except that E1 is evaluated only once. In += and -=, E1 shall either have arithmetic type or be a pointer to a possibly cv-qualified completely-defined object type. In all other cases, E1 shall have arithmetic type.
So, result= a &= b; is the same as
result = (a = (a & b));
or textually:
store the result of bitwise and between a and b into a and result.

It's not a keyword, it's an expression operator. Found here: https://en.cppreference.com/w/cpp/language/expressions
It is the bit-wise AND operator combined with the assignment operator. On primitive types, it performs a bit-wise AND on the two parameters and then assigns the result to the first parameter.
uint32_t a = 0b00110101;
uint32_t b = 0b01101011;
a &= b;
//Equivalent: a = a & b;
assert(a == 0b00100001);

foo &= bar is short for foo = foo & bar, for built-in types. But, it's an operator that can be reimplemented by user defined types, so it's semantics could be changed for such.

There’s one relevant difference between a = a & b; and a &= b;. The former creates a temporary equal to a & b and assigns that temporary value to a. The latter updates a in place.
For built-in types, that doesn’t matter: any compiler will just optimize either expression to the same machine-language instruction. If you define a class and overload the operators, though, writing a = a & b; might end up creating a temporary object, copying a bunch of data to it, and then copying all its data somewhere else. While there are ways to mitigate this problem, a &= b; is in theory more efficient.

Related

bit operator promotion on function return value

With code:
#include <cstdint>
uint8_t a() { return 5; }
auto b() {
uint8_t c = 6;
c |= a(); // Warning here
return c;
}
auto d() {
uint8_t c = 6;
uint8_t d = a();
c |= d;
return c;
}
g++ warns (using -Wconversion):
<source>:7:12: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]
I assume the problem is related to integer promotion for bit operations, but in the second function d() I assign it to a variable first and then there is no warning.
(clang does not warn about this, only g++)
Can this be solved by casting instead of variable assignment?
Why does it behave differently when I use a function?
Compiler explorer with the above: https://godbolt.org/z/q9eMVT
I think this is a bug in GCC. Based on [expr.ass]/7, the expression
x |= y
is equivalent to
x = x | y
except that a is only evaluated once. In a bitwise inclusive OR, like other bitwise operations, as also noted in CoryKramer's comment above, the usual arithmetic conversions will first be performed on both operands [expr.or]/1. Since both our operands are of type std::uint8_t, the usual arithmetic conversions are simply the integral promotions [expr.arith.conv]/1.5. Integral promotion on std::uint8_t should mean that both operands are converted to int [conv.prom]/1. No further conversions of the operands should be required, since the converted types of both operands are the same. Finally, the int resulting from the | expression is then converted back to std::uint8_t and stored in the object referred to by x [expr.ass]/3. I would assume that this last step is what triggers the warning in some cases. However, there is no way that the result of a bitwise logical OR between two std::uint8_t could possibly not be representable in an std::uint8_t, no matter whether we convert to int (which is guaranteed to be larger) and back along the way. Thus, the warning is unnecessary here, which is probably why it is normally not produced.
The only difference I see between your first and your second version is that a() is an rvalue while d is an lvalue. The value category should have no influence on the behavior of the usual arithmetic conversions, however. Thus, the warning—unnecessary or not—should at least be produced consistently. As you have noted yourself, other compilers, such as clang, will not produce a warning here. Furthermore, the issue seems to be oddly-specific to the involvement of function calls in compound assignment. As noted by SergeyA in another comment above, GCC will not produce the warning in the equivalent form of c = c | a(). Using other kinds of rvalues in place of the function call, such as, for example, the result of casting a literal
c |= static_cast<std::uint8_t>(42);
will also not produce a warning in GCC. But as soon as there is a function call on the right-hand side of the expression, even if the result of the function call is not used at all, e.g., in
c |= (a(), static_cast<std::uint8_t>(5));
the warning appears. Thus, I would conclude that this is a bug in GCC and it would be great if you would write a bug report. …

Why is there no operator ~= in C++? [duplicate]

This question already has an answer here:
Why doesn't C++ have the ~= and != operators? [closed]
(1 answer)
Closed 5 years ago.
int main()
{
unsigned int a = 0;
unsigned int b = 0;
a ^= b; // ok
a |= b; // ok
a &= b; // ok
a = ~b; // ok
a ~= b; // error : expected ';' after expression
}
^=, |=, and &= are all legal.
Why isn't ~= legal in C++?
Because ~ is an unary operator, not binary.
The short form op= applies to binary operators only, when the first operand is the destination.
~ is only ever a unary operator.
The contraction of a = a # b to a #= b for an arbitrary operator # only makes sense if it takes two arguments; i.e. if # is a binary operator.
Why isn't ~= legal in C++?
That's because C++ does not contain an ~= operator; ~= are just two separate tokens.
As for why C++ wasn't designed that way, you'd have to ask the designer, but I think it's safe to say that it's because C++ was based on C originally and has the same operators as C, with only a few new operators added for the new language features (i.e. :: and ->*).
So you should probably ask this question again for C.
Because the "operator assignments" (like +=, ^=) are based on the original operation having two operands.
For example, a + b has two operands of +, and a += b gives the same net effect as a = a + b.
Operator ~ is a unary operator i.e. it accepts one operand. So a = ~b makes sense, but a = a~b does not. Since a = a~b doesn't make sense, neither does a ~= b.

What is the use of >>= or <<= compound assignment in c++?

I am new to c++ and haven't seen such a declaration in programs anywhere. Please help me
For integral type
i >>= 3;
is equal to:
i = i >> 3;
ie bitwise shift variable i right (in his case) in place.
For user defined classes it can be overloaded to whatever functionality author wants this operator to implement
Use of compound assignment operators is encouraged since it evaluates the LHS only once.
From the C++11 Standard:
5.17 Assignment and compound assignment operators
...
7 The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.
If you have a function that returns a reference to an object, it is more efficient and less error prone when you use a compound assignment operator.
E.g.
std::vector<int> v(10, 1);
v[4] <<= 2;
is better than
std::vector<int> v(10);
v[4] = v[4] << 2;
those are called compound assignment operators. There are almost 10 of them in C/C++ language. ex += -= *= <<= >>=. When one of the operand is same as the variable to which final result to be assigned is same, in a binary operation this can be used as a short hand syntax. Ex a=a+b can be written as a+=b. in the same a=a<<2 can be written as a<<=2. This type of syntactic sugar is also supported by other languages too.
Left shift assignment <<=
x <<= y Shift x left by y bits
Right shift assignment >>=
x >>= y Shift x right by y bits
Read This to know more about bitwise operators in c++.
In c in general, an operator with an equals following it is shorthand for "do the operation, and put the result back in the same variable."
x = x >> 4; // shift x right by 4 bits
x >>= 4; // exactly the same as above
x <<= 3; // shift x left by 3 bits
This assumes that x is a numeric type like long, unsigned short, etc.
If x is not numeric, then the original programmer probably defined the operation as an overloaded operator in c++, for example, lopping off n characters from a string.

Do the &= and |= operators for bool short-circuit?

When writing code like this in C++:
bool allTrue = true;
allTrue = allTrue && check_foo();
allTrue = allTrue && check_bar();
check_bar() will not be evaluated if check_foo() returned false. This is called short-circuiting or short-circuit evaluation and is part of the lazy evaluation principle.
Does this work with the compound assignment operator &=?
bool allTrue = true;
allTrue &= check_foo();
allTrue &= check_bar(); //what now?
For logical OR replace all & with | and true with false.
From C++11 5.17 Assignment and compound assignment operators:
The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.
However, you're mixing up logical AND which does short-circuit, and the bitwise AND which never does.
The text snippet &&=, which would be how you would do what you're asking about, is nowhere to be found in the standard. The reason for that is that it doesn't actually exist: there is no logical-and-assignment operator.
The short-circuit (i.e. lazy) evaluation is only for logical && and ||. Bitwise & and | evaluate both arguments.
No, they do not cut-short.
Note that the &= and |= operators are formed as &+= and |+=. Bit operators & and | does not perform shortcut evaluation.
Only boolean operators && and || perform it.
It means, that a shortcutting operator would have to be traditionally named &&= and ||=. Some languages provide them. C/C++ does not.
The code allTrue &= check_foo(); is equivalent to allTrue = allTrue & check_foo()
In which you are using bitwise AND and no lazy evaluation is performed.
The bitwise AND must take two arguments who's binary representation has the same length, and useslogical AND operation to compare each corresponding pair of bits.
First: a &= b; is not the same as a = a && b;. a &= b; means a = a & b;. In C/C++ there is no a &&= b;.
Logical AND a && b is bit like a test for 1 bit. If the first "bit" is already 0, than the result will always be 0 no matter the second. So it is not necessary to evaluate b if the result is already clear from a. The C/C++ standard allows this optimization.
Bitwise AND a & b performs this test for all bits of a and b. So b needs to be evaluated if at least one bit in a would be non-zero. You could perhaps wish that if a==0, than b would not be evaluated, but this optimization is not allowed in C/C++.
Since & is a bit operation, check_foo()` will be evaluated irrespective of value of result
result = false;
result &= check_foo(); // check_foo() is needless called
However, check_foo() will not be called if you use && and result is false as in:
result = false;
result = result && check_foo(); // check_foo() is not called, the bitwise operator shortcircuits

Variable initialization in C++: a unique method

Recently I came across this piece of code. I don't know why I never saw this kind of syntax in all my "coding life".
int main()
{
int b;
int a = (b=5, b + 5);
std::cout << a << std::endl;
}
a has value of 10. What exactly is this way of initialization called? How does it work?
This statement:
int a = (b=5, b + 5);
Makes use of the comma operator. Per Paragraph 5.18/1 of the C++11 Standard:
[...] A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded value
expression (Clause 5).83 Every value computation and side effect associated with the left expression
is sequenced before every value computation and side effect associated with the right expression. The type
and value of the result are the type and value of the right operand; the result is of the same value category
as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right
operand is a temporary (12.2), the result is that temporary.
Therefore, your statement is equivalent to:
b = 5;
int a = b + 5;
Personally, I do not see a reason for using the comma operator here. Just initialize your variable the easily readable way, unless you have a good reason for doing otherwise.
operator , evaluates arguments one after another and return the last value
It may be used not only in initialization
The comma , operator allows you to separate expressions. The compount statement made by
exp1, exp2, ..., expn
evaluates to expn.
So what happens is that first b is set to 5 and then a is set to b + 5.
A side note: since , has the lowest precedence in the table of operators the semantics of
int a = b = 5, b+5;
is different from
int a = (b = 5, b+5);
because the first is parsed as (int a = b = 5), b + 5
When used in an expression the comma operator will evaluate all of its operands (left-to-right) and return the last.
The initialization is called copy initialization. If you ignore the complex expression on the right, it's the same as in:
int a = 10;
This is to be contrasted with direct initialization, which looks like this:
int a(10);
(It's possible that you were separately confused about how to evalue a comma expression. Please indicate if that's the case.)