Question: What does the "=" operator return?

Question

What does the "=" operator return?

Answers 4
Added at 2017-01-02 19:01
Tags
Question

From what I understand, C++ works from left to right. For example, if I do:

std::cout << "output" << "stream";

C++ first begins at the leftmost thing (std::cout), next there is the << operator which takes the string literal at the right ("output") and put it to the object at the left of the operator (std::cout). C++ then returns the object at the left of the operator (std::cout) and then continues the code, another << operator.

What does the "=" operator return?

If I do

double wage = 5;
double salary = wage = 9999.99;

I thought the "=" operator would return only the left or right operand of the "=". So, with my logic, in the line of the salary initialization, salary is initialized with the value of wage, then the "=" operator returns either salary or wage (let's say salary), then it assign 9999.99 to salary, but wage is gone, it should keep its value of 5.

But when I check the value of "salary" and "wage" after the "salary" initialization, both have a value of 9999.99. If I apply the same logic I used with std::cout above, there should be only one variable, either "salary" or "wage", with a value of 9999.99

Answers to

What does the &quot;=&quot; operator return?

nr: #1 dodano: 2017-01-02 19:01

Consult the operator precedence table. The assignment operator, =, is "right to left" associativity. So the expression is effectively evaluated as:

double salary = (wage = 9999.99);

With wage = 9999.99 occurring first.

nr: #2 dodano: 2017-01-02 19:01

The assignment operator is right to left associative, and generally speaking returns its left operand by reference. Generally speaking meaning, this is true for all built in types, library types that I can think of, and that's how you are expected to write assignment operators.

That means that

double salary = wage = 9999.99;

Is exactly the same as

wage = 9999.99;
double salary = wage;

Note that in the second line here, salary gets set to wage, not 9999.99. The distinction doesn't matter here but in some cases it may. For instance (thanks to Justin Time for example):

double salary = --(wage = 9999.99);

salary clearly gets a value of 9998.99 regardless, but its important to note that wage does as well; if assignment returned the right argument instead then wage would still end up being 9999.99 because the temporary would get decremented instead after the assignment to wage had occurred.

As Ben Voigt points out in the comments below, while my answer is correct, the example from the question that I use is slightly misleading. This is because despite the = token showing up twice in the code in question, double salary = wage = 9999.99 actually is not invoking assignment twice, but rather invoking assignment and then constructing a new double. The example would be better continued as follows:

double salary = 0.0;
salary = wage = 9999.99;

Now we are genuinely chaining assignment operators, and all my previous comments about precedence and returns are applicable to the second line of code here.

nr: #3 dodano: 2017-01-03 00:01

Expressions have a value and a side-effect. The value (what you're calling the "return") is what will be used for another operator that this expression is an operand of. (It's still computed even if there is no such operator). The process of finding the value of an expression is called the value computation.

The value may have any value category. The side-effect is anything else that happens, such as updating a memory location, or calling a function.

In general the side-effect may or may not occur before the value is used in a larger sub-expression. Famously, the postfix-++ operator may have quite a big difference in timing between the value and the side-effect.

For assignment expressions, the value is the left operand (with value category "lvalue"), and the side-effect is that the object designated by the left operand is updated to hold the value of the right operand; and the side-effect is sequenced before the value computation, which guarantees that if a larger expression uses the value, then it designates a memory location that has already got the new value stored in it.

If the code were:

salary = wage = 9999.0;

then the operator associativity rules would mean that it is salary = (wage = 9999.0);. The inner expression has value wage with value category lvalue, and the side-effect of the inner expression is that the variable named wage gets 9999.0 stored in it, and the sequencing of assignment operators guarantees that the result is already stored there before we move onto the next outer-most expression.

Then we have salary = X, where X is the value described in the previous paragraph, i.e. it is equivalent to salary = wage;


Note that your actual code is a declaration, and the first = symbol in a declaration is not the assignment operator. Instead it is a grammatical marker that an initializer is coming; your code is the same as:

double salary { wage = 9999.0 };
nr: #4 dodano: 2017-01-03 00:01

In your example

double wage = 5;
double salary = wage = 9999.99;

… there is only one assignment, namely

wage = 9999.99

The other instances of = are part of the initialization syntax.

The assignment returns a reference to the object that's assigned to, in this case wage. So the example is equivalent to

double wage = 5;
wage = 9999.99;
double salary = wage;

If your example is rewritten to use multiple assignments,

double wage;
double salary;
wage = 5;
salary = wage = 9999.99;

… then it becomes important that assignment operators are right-associative. Thus the last line is equivalent to

salary = (wage = 9999.99);

… where the parenthesized expression returns a reference to wage.


The standard containers require that a user defined assignment operator returns a reference to the assigned object. The core language has no such requirement, so one may be tempted to use void. I.e. guaranteed efficiency, not supporting unholy side-effect based code, and more concise implementation, all of which are good:

struct S
{
    void operator=( S );    // OK with respect to core language rules.
};

However, in order to delete or default an assignment operator the declaration of the operator must give it reference return type:

struct T
{
    auto operator=( T const& ) -> T&    // But `void` won't work here.
        = delete;
};

On the third & gripping hand, the verbose, no-guaranteed-efficiency and ungood-support-for-side-effect-based-code operator= can be expressed in terms of a separate void member function, which can even be an operator:

struct U
{
    void assign( U other );

    auto operator=( U const& other )
        -> U&
    {
        assign( other );
        return *this;
    }
};
Source Show
◀ Wstecz