assignment operator

S

subramanian100in

For a class Test, we write the assignment operator as

Test & Test::eek:perator=(const Test & rhs);

instead of

const Test & Test::eek:perator=(const Test & rhs);

that is, the return type of operator=( ) is just a reference, not a
reference to const object.

is it because to allow for expressions such as

(x = y) + z;
where x, y, z are of type Test ?

What would happen if we allowed "const Test &" as the return type of
Test::eek:perator=( ) ?

Kindly clarify.

Thanks
V.Subramanian
 
G

Guest

For a class Test, we write the assignment operator as

Test & Test::eek:perator=(const Test & rhs);

instead of

const Test & Test::eek:perator=(const Test & rhs);

that is, the return type of operator=( ) is just a reference, not a
reference to const object.

is it because to allow for expressions such as

(x = y) + z;
where x, y, z are of type Test ?

What would happen if we allowed "const Test &" as the return type of
Test::eek:perator=( ) ?

Kindly clarify.

I would say it is because there is no reason to return a const reference
(since the object referred to is not const) and it does allow us to do
some things that a const reference would not. Your example above "(x =
y) + z;", however, should work even with a const reference, the
following would not "(x = y) += z;", but on the other hand you should
probably write such code in the first place. Notice also that the other
unary operators, such as +=, *=, etc., return a non-const reference.
 
K

Kai-Uwe Bux

For a class Test, we write the assignment operator as

Test & Test::eek:perator=(const Test & rhs);

instead of

const Test & Test::eek:perator=(const Test & rhs);

that is, the return type of operator=( ) is just a reference, not a
reference to const object.

is it because to allow for expressions such as

(x = y) + z;
where x, y, z are of type Test ?

Not really. The following compiles just fine:

class Test {
public:

Test const & operator= ( Test const & other ) {
return ( *this );
}

Test operator+ ( Test const & rhs ) const {
return ( *this );
}

};

int main ( void ) {
Test a;
Test b;
Test c;
( a = b ) + c;
}

It's more expressions like this:

swap( a=b, c );

Now, you would not really want that in your code anyway.
What would happen if we allowed "const Test &" as the return type of
Test::eek:perator=( ) ?

Well, it is allowed. What happens, is that you could not call non-const
methods on the returned reference. Since the return is usually ignored, it
does not really matter all that much. You could even make operator= return
void. The compiler will not care.


Best

Kai-Uwe Bux
 
M

Michal Nazarewicz

Erik Wikström said:
I would say it is because there is no reason to return a const reference
(since the object referred to is not const) and it does allow us to do
some things that a const reference would not. Your example above "(x =
y) + z;", however, should work even with a const reference,

How come? There is no "operator=(const Test&)const", so how could it
work?
the
following would not "(x = y) += z;", but on the other hand you should
probably write such code in the first place. Notice also that the other
unary operators, such as +=, *=, etc., return a non-const reference.

Those are not unary operators and they return the type you specify which
should be const reference.

Programmers don't expect expressions such as "(a = b) = c" or "(a += b)
= c" to work and thus they should not work. Operator overloading is not
for creating new syntax.
 
K

Kai-Uwe Bux

Michal said:
How come? There is no "operator=(const Test&)const", so how could it
work?


Those are not unary operators and they return the type you specify which
should be const reference.

They are assignment operators; and for built in types, they return lvalues.

Programmers don't expect expressions such as "(a = b) = c" or "(a += b)
= c" to work and thus they should not work.

Those programmers expect wrongly. These expressions work just fine for built
in types:

#include <iostream>

int main ( void ) {
int a = 3;
int b = 2;
int c = 4;
int d = 3;
( a += b ) = ( c = d );
std::cout << a;
std::cout << b;
std::cout << c;
std::cout << d;
}

Operator overloading is not for creating new syntax.

In the above code, the syntax you abhor is neither new nor created by
operator overloading.



Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Kai-Uwe Bux said:
They are assignment operators; and for built in types, they return
lvalues.



Those programmers expect wrongly. These expressions work just fine for
built in types:

#include <iostream>

int main ( void ) {
int a = 3;
int b = 2;
int c = 4;
int d = 3;
( a += b ) = ( c = d );
std::cout << a;
std::cout << b;
std::cout << c;
std::cout << d;
}



In the above code, the syntax you abhor is neither new nor created by
operator overloading.

Since the discussion was about syntax, I forgot to mention semantics:

( a = b ) = c

is well-defined for user defined types since the overloaded operators are
functions and create sequence points. For built in types, the variable a is
modified twice between sequence points and the behavior is undefined.



Best

Kai-Uwe Bux
 
G

Guest

How come? There is no "operator=(const Test&)const", so how could it
work?

Because the plus operator should take const references as arguments and
be declared as const.

class Foo
{
public:
const Foo& operator=(const Foo& rhs) { return *this; }
Foo operator+(const Foo& rhs) const { return Foo(); }
};

int main()
{
Foo a, b, c;

Foo d = (a = b) + c;

Foo e = a + b;
}
Those are not unary operators and they return the type you specify which
should be const reference.

Sorry, yes they are not unary operators, I meant assignments operators.
And of course they return whatever type you want them to return, but
they *should* return a non-const reference to themselves (or a type
which is convertible to that).
Programmers don't expect expressions such as "(a = b) = c" or "(a += b)
= c" to work and thus they should not work. Operator overloading is not
for creating new syntax.

I expect them to work, the most important thing when overloading
operators is to keep the semantics sane, which usually means that it
should be compatible with the semantics of built-in types. Of course it
is really bad style to actually use such code, but it should work.
 
M

Michal Nazarewicz

Erik Wikström said:
Because the plus operator should take const references as arguments and
be declared as const.

Ah... Sorry... You are of course right.
 
M

Michal Nazarewicz

Kai-Uwe Bux said:
Those programmers expect wrongly. These expressions work just fine for built
in types:

Everyday you learn something new. I wasn't aware that such think is
possible in C++ (it's not in C from what I know). Thanks for pointing
this out.
 
J

James Kanze

For a class Test, we write the assignment operator as
Test & Test::eek:perator=(const Test & rhs);
instead of
const Test & Test::eek:perator=(const Test & rhs);
that is, the return type of operator=( ) is just a reference, not a
reference to const object.
is it because to allow for expressions such as
(x = y) + z;
where x, y, z are of type Test ?
What would happen if we allowed "const Test &" as the return type of
Test::eek:perator=( ) ?

The only real reason for the return value to be non-const is
because that is what the built-in operator= does, so applying
the principle of least surprise...

I believe that the standard "justification" is to allow things
like:

class D
{
public:
C& D::setX( C newValue )
{
return myC = newValue ;
}

private:
C myC ;
} ;

I'll admit that I'm not totally convinced; a return statement
should return a value, and not modify some value. My operator=
returned const references for a long time, and this never caused
any problems in any of the code I had to maintain. But since
the built-in operator= evaluates to a non-const lvalue, I've
adopted the non-const reference, just to be as much like it as
possible.
 
J

James Kanze

Everyday you learn something new. I wasn't aware that such think is
possible in C++ (it's not in C from what I know). Thanks for pointing
this out.

It's a significant difference. In C, none of the assignment
operators, nor increment nor decrement, result in lvalues. In
C++, all of the assignment operators, along with pre-increment
and pre-decrement, result in lvalues---in general, if an
expression requires an lvalue, and the value of that expression
is the value of the lvalue, after evaluation of the expression,
the expression results in an lvalue in C++.
 
J

James Kanze

On 2007-09-23 13:32, Michal Nazarewicz wrote: [...]
Programmers don't expect expressions such as "(a = b) = c" or "(a += b)
= c" to work and thus they should not work. Operator overloading is not
for creating new syntax.
I expect them to work, the most important thing when overloading
operators is to keep the semantics sane, which usually means that it
should be compatible with the semantics of built-in types. Of course it
is really bad style to actually use such code, but it should work.

The above will *not* work with the built-in operators. It
results in undefined behavior, due to an object being modified
twice without an intervening sequence point.

C++ has different rules than C regarding lvalues. I rather
suspect that this is related to the introduction of references
(which make lvalue-ness far more directly observable). In C++,
you can write:

int a, b ;
int& r = (a = b) ;
int* p = &(a = b) ;

In C, the second line, of course, is irrelevant, and the third
line is illegal.

Note that something like the second line will occur if you write
something like:
a = b + (c = d) ;
for a user defined type. Since this sort of thing is perfectly
legal for e.g. int or double, it does seem like the language
should allow it for user defined types as well.
 
A

Andrew Koenig

C++ has different rules than C regarding lvalues. I rather
< suspect that this is related to the introduction of references
< (which make lvalue-ness far more directly observable). In C++,
< you can write:

int a, b ;
int& r = (a = b) ;
int* p = &(a = b) ;
In C, the second line, of course, is irrelevant, and the third
line is illegal.

You're pretty much right about the reason--I happen to have been there when
the issue first came up, so I know the history.

Here's the key question: You have a fragment of code that looks like this:

// ...
x = y;
return x;

Can you replace that code with

return x=y;

without changing the meaning of the program?

In C, the answer is always yes. In C++, the situation is trickier:

int& foo(int& x, int y)
{
x = y;
return x;
}

If = were defined on int objects the same way as in C, then changing this
function to

int& foo(int& x, int y)
{
return x = y;
}

would cause it to stop compiling, because in C, = yields an rvalue.

So the fact that a function can potentially return a reference makes it
necessary to redefine = to return an value.

It turns out that this redefinition is still compatible with C, because it
is impossible to write a C program that is capable of detecting whether =
returns an lvalue or an rvalue.

Once the built-in = operator returns an lvalue, it makes sense for
user-defined ones to behave similarly.

Incidentally, the same reasoning applies to ++x, --x, and x?y:z.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top