Is The Assignment Operator Really "Just" an Operator

Is the assignment operator really just an operator?

From the doc: https://ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#assign

Assignment expression are used to assign objects to the variables or such. Assignments sometimes work as declarations for local variables or class constants. The left hand side of the assignment expressions can be either:

variables
variables `=' expression

On the right there is an expression, so the result of the expression is assigned to the variable.

So, you should look for expressions (*) before following the precedence.

1 && a = 3 are basically two "chained" expressions:

3 and 1 && 3

Maybe it is more readable as:
1 && a = 3 + 4 where the expressions are 3 + 4 and 1 && 7, see:

1 && a = 3 + 4 #=> 7
1 && 7 #=> 7
res = 1 && a = 3 + 4
res #=> 7

(*) The precedence table also helps to find the expression (Find the precedence table in the linked doc at the Operator expressions paragraph):

What's above the = in the table "forms" an expression to be assigned by =, what's below does not.

For example:

1 + 3 and 2 + 4 #=> 4
a = 1 + 3 and b = 2 + 4 #=> 4
(a = 1 + 3) and (b = 2 + 4) #=> 4
a = (1 + 3 and b = 2 + 4) #=> 6

You can also check these examples respect to the precedence table:

1 && 3 #=> 3
1 && a = 3 #=> 3
a #=> 3

3 and 1 #=> 3
3 and b = 1 #=> 3
b #=> 1

2 ** c = 2 + 1 #=> 8
c #=> 3

d = 2 ** 3
d #=> 8

e = 3
e **= 2
e #=> 9

What are the differences between = and - assignment operators?

What are the differences between the assignment operators = and <- in R?

As your example shows, = and <- have slightly different operator precedence (which determines the order of evaluation when they are mixed in the same expression). In fact, ?Syntax in R gives the following operator precedence table, from highest to lowest:


‘-> ->>’ rightwards assignment
‘<- <<-’ assignment (right to left)
‘=’ assignment (right to left)

But is this the only difference?

Since you were asking about the assignment operators: yes, that is the only difference. However, you would be forgiven for believing otherwise. Even the R documentation of ?assignOps claims that there are more differences:

The operator <- can be used anywhere,
whereas the operator = is only allowed at the top level (e.g.,
in the complete expression typed at the command prompt) or as one
of the subexpressions in a braced list of expressions.

Let’s not put too fine a point on it: the R documentation is wrong. This is easy to show: we just need to find a counter-example of the = operator that isn’t (a) at the top level, nor (b) a subexpression in a braced list of expressions (i.e. {…; …}). — Without further ado:

x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1

Clearly we’ve performed an assignment, using =, outside of contexts (a) and (b). So, why has the documentation of a core R language feature been wrong for decades?

It’s because in R’s syntax the symbol = has two distinct meanings that get routinely conflated (even by experts, including in the documentation cited above):

  1. The first meaning is as an assignment operator. This is all we’ve talked about so far.
  2. The second meaning isn’t an operator but rather a syntax token that signals named argument passing in a function call. Unlike the = operator it performs no action at runtime, it merely changes the way an expression is parsed.

So how does R decide whether a given usage of = refers to the operator or to named argument passing? Let’s see.

In any piece of code of the general form …

‹function_name›(‹argname› = ‹value›, …)
‹function_name›(‹args›, ‹argname› = ‹value›, …)

… the = is the token that defines named argument passing: it is not the assignment operator. Furthermore, = is entirely forbidden in some syntactic contexts:

if (‹var› = ‹value›) …
while (‹var› = ‹value›) …
for (‹var› = ‹value› in ‹value2›) …
for (‹var1› in ‹var2› = ‹value›) …

Any of these will raise an error “unexpected '=' in ‹bla›”.

In any other context, = refers to the assignment operator call. In particular, merely putting parentheses around the subexpression makes any of the above (a) valid, and (b) an assignment. For instance, the following performs assignment:

median((x = 1 : 10))

But also:

if (! (nf = length(from))) return()

Now you might object that such code is atrocious (and you may be right). But I took this code from the base::file.copy function (replacing <- with =) — it’s a pervasive pattern in much of the core R codebase.

The original explanation by John Chambers, which the the R documentation is probably based on, actually explains this correctly:

[= assignment is] allowed in only two places in the grammar: at the top level (as a complete program or user-typed expression); and when isolated from surrounding logical structure, by braces or an extra pair of parentheses.


In sum, by default the operators <- and = do the same thing. But either of them can be overridden separately to change its behaviour. By contrast, <- and -> (left-to-right assignment), though syntactically distinct, always call the same function. Overriding one also overrides the other. Knowing this is rarely practical but it can be used for some fun shenanigans.

Overloading the assignment operator vs. providing an implicit constructor

If C defines a non-explicit constructor taking an int then you don't necessarily need an assignment operator taking an int, but it may end up being more efficient.

If operator=(int) exists:

C c; //default constructor
c = 5; //assignment from int

If C(int) exists and operator=(int) does not:

C c; //default constructor
c = 5; //construction of temporary from int, then assignment from C

If move semantics are efficient for C then the latter may be acceptable. If not, you might want to define that operator=(int) anyway. Ultimately, this depends on a number of factors, like how often you are going to be assigning to C, how expensive copies are etc. Understand the issues, work out how they apply to your class, then choose the most reasonable solution.

Is assignment operator a sequence point under C++17? and what would be the result of this expression?

There's no sequence points now: we have sequenced-before and sequenced-after. When you have an operator= call (or any other operator@= call - built-in operator= or user-defined call), right-hand side is sequenced-before left-hand side. So ++i = i++ is valid in C++17, with i++ sequenced-before ++i.

Before C++17, as you wrote, it was UB.

What's the difference between assignment operator and copy constructor?

A copy constructor is used to initialize a previously uninitialized object from some other object's data.

A(const A& rhs) : data_(rhs.data_) {}

For example:

A aa;
A a = aa; //copy constructor

An assignment operator is used to replace the data of a previously initialized object with some other object's data.

A& operator=(const A& rhs) {data_ = rhs.data_; return *this;}

For example:

A aa;
A a;
a = aa; // assignment operator

You could replace copy construction by default construction plus assignment, but that would be less efficient.

(As a side note: My implementations above are exactly the ones the compiler grants you for free, so it would not make much sense to implement them manually. If you have one of these two, it's likely that you are manually managing some resource. In that case, per The Rule of Three, you'll very likely also need the other one plus a destructor.)

Purpose of assignment operator overloading in C++

Actually, after seeing juanchopanza's answer (which was deleted), I think I ended up figuring it out myself.

Copy-assignment operators allow classes like basic_string to avoid allocating resources unnecessarily when they can re-use them (in this case, memory).

So when you assign to a basic_string, an overloaded copy assignment operator would avoid allocating memory, and would just copy the string data directly to the existing buffer.

If the object had to be destroyed and constructed again, the buffer would have to be reallocated, which would be potentially much more costly for a small string.

(Note that vector could benefit from this too, but only if it knew that the elements' copy constructors would never throw exceptions. Otherwise it would need to maintain its exception safety and actually perform a copy-and-swap.)

copy assignment operator clarification

What I'm finding confusing is, why are they including the line delete[] name;?

When using new and new[] to manage memory, the rule is thumb is that they should have a matching delete or delete[]. Presumably, name was previously allocated with new[] (either in the constructor or by a previous assignment), so before reassigning it on the next line with name = local_name;, we need to delete[] it.

I couldn't understand why the function checks if(this != &that)

This check is a sanity check. If you assign an object to itself, then there is no need to do anything. In fact, it could cause problems because if you delete[] name; when this points to the same object as that references, then you can no longer copy that.name because its memory has been freed.

Why can't I just write: name = that.name or

This will make name in two different instances point to the same memory. In some cases, such shared memory is not only useful but desired. However, you have to be careful because if one of the instances executes delete[] on its name then name in the other instance is no longer valid.



Related Topics



Leave a reply



Submit