[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

Why the copy assignment operator is written to return non-const reference to this?

Singulus

11/22/2008 10:40:00 PM

Introduction:

It is good advice to write the return type of a function that returns
by value as a const:

SomeType GetSomeType(...); // BAD
const SomeType GetSomeType(...); // GOOD

The above is good because it prevents the function return value to be
used as a temporary:

GetSomeType().DoStuff();

This is especially useful in the case of operator functions that
creates objects, for example:

Vector3 operator+( const Vector & lhs, const Vector & rhs ); // BAD
const Vector3 operator+( const Vector & lhs, const Vector & rhs ); //
GOOD
(vec1 + vec2).Normalize(); // BAD variant pass this non-nonse, GOOD
stops it at compile time


The Question:

Why the same principle is not used for the copy assignment of classes?

Currently, the canonical copy assignment operator function prototype
is written like this:

class SomeType
{
SomeType & operator=( const SomeType & lhs );
};

This will pass a non-sense code like the following. The non-sense is
not the same as with the temporary objects but still is not the best
that can be done to restrict the writing of non-sense which is quite
easily and often unforgiving with C++:

SomeType t, u;
....
(t = u).DoStuff();

If we write simply append a const, this will still preserve the
behaviour when we have a chain of assignments because the function
itself takes a const reference to an object of the same class, so it
matches with the return value:

class SomeType
{
const SomeType & operator=( const SomeType & lhs );
};

SomeType t, u, v;
....
t = u = v;
21 Answers

Ian Collins

11/22/2008 11:22:00 PM

0

Singulus wrote:
> Introduction:
>
> It is good advice to write the return type of a function that returns
> by value as a const:
>
> SomeType GetSomeType(...); // BAD
> const SomeType GetSomeType(...); // GOOD
>
> The above is good because it prevents the function return value to be
> used as a temporary:
>
> GetSomeType().DoStuff();
>
Why is this good?

> This is especially useful in the case of operator functions that
> creates objects, for example:
>
> Vector3 operator+( const Vector & lhs, const Vector & rhs ); // BAD
> const Vector3 operator+( const Vector & lhs, const Vector & rhs ); //
> GOOD
> (vec1 + vec2).Normalize(); // BAD variant pass this non-nonse, GOOD
> stops it at compile time
>
Why is this good?
>
> The Question:
>
> Why the same principle is not used for the copy assignment of classes?
>
Because it isn't good?

--
Ian Collins

Singulus

11/23/2008 12:38:00 AM

0

On Nov 23, 1:21 am, Ian Collins <ian-n...@hotmail.com> wrote:
> Singulus wrote:
> > Introduction:
>
> > It is good advice to write the return type of a function that returns
> > by value as a const:
>
> > SomeType GetSomeType(...); // BAD
> > const SomeType GetSomeType(...); // GOOD
>
> > The above is good because it prevents the function return value to be
> > used as a temporary:
>
> > GetSomeType().DoStuff();
>
> Why is this good?
>
> > This is especially useful in the case of operator functions that
> > creates objects, for example:
>
> > Vector3 operator+( const Vector & lhs, const Vector & rhs ); // BAD
> > const Vector3 operator+( const Vector & lhs, const Vector & rhs ); //
> > GOOD
> > (vec1 + vec2).Normalize(); // BAD variant pass this non-nonse, GOOD
> > stops it at compile time
>
> Why is this good?
>
> > The Question:
>
> > Why the same principle is not used for the copy assignment of classes?
>
> Because it isn't good?
>
> --
> Ian Collins

Please, elaborate.

joseph cook

11/23/2008 12:58:00 AM

0

On Nov 22, 5:39 pm, Singulus <singu...@gmail.com> wrote:
> Introduction:
>
> It is good advice to write the return type of a function that returns
> by value as a const:
>
> SomeType GetSomeType(...); // BAD
> const SomeType GetSomeType(...); // GOOD
>
> The above is good because it prevents the function return value to be
> used as a temporary:
>
> GetSomeType().DoStuff();
>
> This is especially useful in the case of operator functions that
> creates objects, for example:
>
> Vector3 operator+( const Vector & lhs, const Vector & rhs ); // BAD
> const Vector3 operator+( const Vector & lhs, const Vector & rhs ); //
> GOOD
> (vec1 + vec2).Normalize(); // BAD variant pass this non-nonse, GOOD
> stops it at compile time
>
> The Question:
>
> Why the same principle is not used for the copy assignment of classes?

Primarily because base types (int, etc) return references, and its
considered good practice for your new types to behave semantically
like the built-in types. Someone using your class might expect as
much.

Is this a hard and fast rule? No. I don't expect you'll get any
grief or find any dangerous dragons down the corridor for returning
const ref.

My advice is to do as you like in this instance.

Joe Cook

Andrey Tarasevich

11/23/2008 1:00:00 AM

0

Singulus wrote:
>
> It is good advice to write the return type of a function that returns
> by value as a const:
>
> SomeType GetSomeType(...); // BAD
> const SomeType GetSomeType(...); // GOOD
>
> The above is good because it prevents the function return value to be
> used as a temporary:

Huh? The return value of both of the above functions is a temporary. One
is qualified with 'const' the other is not, but both are temporaries. So
what does "prevents [...] to be used as a temporary" supposed to mean?

> GetSomeType().DoStuff();

Firstly, this will work for const-methods regardless of whether you add
'const' to the return type or not. What you were trying to do,
apparently, is prohibit modifying operations on the temporary. This
might be a good idea in many cases. But, secondly, there are q few
pretty useful programming techniques out there which rely on this
temporary being modifiable.

> This is especially useful in the case of operator functions that
> creates objects, for example:
>
> Vector3 operator+( const Vector & lhs, const Vector & rhs ); // BAD
> const Vector3 operator+( const Vector & lhs, const Vector & rhs ); //
> GOOD
> (vec1 + vec2).Normalize(); // BAD variant pass this non-nonse, GOOD
> stops it at compile time

So far you failed to explain why one is "GOOD' and other is "BAD".

> The Question:
>
> Why the same principle is not used for the copy assignment of classes?
>
> Currently, the canonical copy assignment operator function prototype
> is written like this:
>
> class SomeType
> {
> SomeType & operator=( const SomeType & lhs );
> };
>
> This will pass a non-sense code like the following. The non-sense is
> not the same as with the temporary objects but still is not the best
> that can be done to restrict the writing of non-sense which is quite
> easily and often unforgiving with C++:
>
> SomeType t, u;
> ...
> (t = u).DoStuff();

Firstly, you started from an attack on "temporaries", and then suddenly
switched to this code, where there are no temporaries. Where's the
connection? Secondly, there's absolutely no "nonsense" in this code.
Some people (me included) might consider it a bad style, by in any case
it is a matter of personal style.

> If we write simply append a const, this will still preserve the
> behaviour when we have a chain of assignments because the function
> itself takes a const reference to an object of the same class, so it
> matches with the return value:
>
> class SomeType
> {
> const SomeType & operator=( const SomeType & lhs );
> };
>
> SomeType t, u, v;
> ...
> t = u = v;

It still won't prevent us from invoking a const-method on the result of
the assignment

class SomeType
{
const SomeType & operator=( const SomeType & lhs );
void DoStuff() const;
};

SomeType t, u;
...
(t = u).DoStuff();

You need to decide what is it you are trying to prevent from happening
and work from there. At this time you don't seem to have a clear idea.

--
Best regards,
Andrey Tarasevich

Ian Collins

11/23/2008 2:01:00 AM

0

Singulus wrote:
> On Nov 23, 1:21 am, Ian Collins <ian-n...@hotmail.com> wrote:
>> Singulus wrote:
>>
>>> The Question:
>>> Why the same principle is not used for the copy assignment of classes?
>> Because it isn't good?
>>
> Please, elaborate.

Andrey's response says it all.

Care to elaborate on why you think one form is good and another bad in
the light of that response?

--
Ian Collins

Daniel T.

11/23/2008 4:14:00 AM

0

Singulus <singulus@gmail.com> wrote:

> The Question:
>
> Why the same principle is not used for the copy assignment of classes?
>
> Currently, the canonical copy assignment operator function prototype
> is written like this:
>
> class SomeType
> {
> SomeType & operator=( const SomeType & lhs );
> };

In this case, the canonical return is "*this". It seems rather pointless
to add the const qualifier to an object that you know the caller already
has as a non-const reference to, doesn't it?

You may think "(t = u).doStuff()" is silly, but since I know that
".doStuff()" operates on the return value of op= (i.e., 't' in this
case) I find it rather a great idea. It may not seem so silly in a
different context, like this:

std::string s;
// later
s.assign(charPtr).append(otherCharPtr);

I often set-up method chains
(http://www.parashift.com/c++-faq-lite/references.ht...). It's
rather standard in SmallTalk as well (in SmallTalk, if a method doesn't
explicitly return something else, it will return self (this).)

Singulus

11/23/2008 8:54:00 AM

0

On Nov 23, 6:14 am, "Daniel T." <danie...@earthlink.net> wrote:
> Singulus <singu...@gmail.com> wrote:
> > The Question:
>
> > Why the same principle is not used for the copy assignment of classes?
>
> > Currently, the canonical copy assignment operator function prototype
> > is written like this:
>
> > class SomeType
> > {
> > SomeType & operator=( const SomeType & lhs );
> > };
>
> In this case, the canonical return is "*this". It seems rather pointless
> to add the const qualifier to an object that you know the caller already
> has as a non-const reference to, doesn't it?
>
> You may think "(t = u).doStuff()" is silly, but since I know that
> ".doStuff()" operates on the return value of op= (i.e., 't' in this
> case) I find it rather a great idea. It may not seem so silly in a
> different context, like this:
>
> std::string s;
> // later
> s.assign(charPtr).append(otherCharPtr);
>
> I often set-up method chains
> (http://www.parashift.com/c++-faq-lite/references.ht...). It's
> rather standard in SmallTalk as well (in SmallTalk, if a method doesn't
> explicitly return something else, it will return self (this).)

Since when method chaining is a good practice in C++? The whole point
of returning reference to this in the copy assignment operator is to
enable chaining of assignments, not to stack another thing to do
(member function calls for example). The const qualifier for the
return reference doen't solve the whole problem of calling member
functions on the result, but since of these 'side effects' or
'chaining' code uses non-const member functions, it will solve the
most frequent case of abuse.

std::string s;
// later
s.assign(charPtr).append(otherCharPtr); // These are all non-const
functions.

// NOW that's is silly...the line does more than one thing.
size_t size = (s = y).GetSize();

Andrey Tarasevich

11/23/2008 9:26:00 AM

0

Singulus wrote:
>
> Since when method chaining is a good practice in C++?

Since forever, of course. "Method chaining", just like everything in
C++, is a technique that can be used correctly or incorrectly. When used
correctly, it is a good practice.

> The whole point
> of returning reference to this in the copy assignment operator is to
> enable chaining of assignments, not to stack another thing to do
> (member function calls for example). The const qualifier for the
> return reference doen't solve the whole problem of calling member
> functions on the result, but since of these 'side effects' or
> 'chaining' code uses non-const member functions, it will solve the
> most frequent case of abuse.
>
> std::string s;
> // later
> s.assign(charPtr).append(otherCharPtr); // These are all non-const
> functions.

.... and there's absolutely no "abuse" in the above example, as far as
method chaining is concerned.

> // NOW that's is silly...the line does more than one thing.
> size_t size = (s = y).GetSize();

And? Obviously 'const' will not help here anyway.

So, you seem to be campaigning against "doing more than one thing in one
line"?.. While thoughtlessly doing too many things "in one line" might
easily result in ugly and silly code, in general case almost every
"line" in C++ does "more than one thing", if you look at it carefully.
Which is why such an unconditional disdain to "more than one thing in
one line" as the one you seem to be demonstrating will not likely result
in anything constructive.

--
Best regards,
Andrey Tarasevich

peter koch

11/23/2008 9:27:00 AM

0

On 22 Nov., 23:39, Singulus <singu...@gmail.com> wrote:
> Introduction:
>
> It is good advice to write the return type of a function that returns
> by value as a const:
>
> SomeType GetSomeType(...); // BAD
> const SomeType GetSomeType(...); // GOOD
>
> The above is good because it prevents the function return value to be
> used as a temporary:
>
> GetSomeType().DoStuff();
>
> This is especially useful in the case of operator functions that
> creates objects, for example:
>
> Vector3 operator+( const Vector & lhs, const Vector & rhs ); // BAD
> const Vector3 operator+( const Vector & lhs, const Vector & rhs ); //
> GOOD
> (vec1 + vec2).Normalize(); // BAD variant pass this non-nonse, GOOD
> stops it at compile time
>
> The Question:
>
> Why the same principle is not used for the copy assignment of classes?
>
> Currently, the canonical copy assignment operator function prototype
> is written like this:
>
> class SomeType
> {
>     SomeType & operator=( const SomeType & lhs );
>
> };
>
> This will pass a non-sense code like the following. The non-sense is
> not the same as with the temporary objects but still is not the best
> that can be done to restrict the writing of non-sense which is quite
> easily and often unforgiving with C++:
>
> SomeType t, u;
> ...
> (t = u).DoStuff();
>
> If we write simply append a const, this will still preserve the
> behaviour when we have a chain of assignments because the function
> itself takes a const reference to an object of the same class, so it
> matches with the return value:
>
> class SomeType
> {
>     const SomeType & operator=( const SomeType & lhs );
>
> };
>
> SomeType t, u, v;
> ...
> t = u = v;

Erik Wikström

11/23/2008 9:42:00 AM

0

On 2008-11-23 09:54, Singulus wrote:
> On Nov 23, 6:14 am, "Daniel T." <danie...@earthlink.net> wrote:
>> Singulus <singu...@gmail.com> wrote:
>> > The Question:
>>
>> > Why the same principle is not used for the copy assignment of classes?
>>
>> > Currently, the canonical copy assignment operator function prototype
>> > is written like this:
>>
>> > class SomeType
>> > {
>> > SomeType & operator=( const SomeType & lhs );
>> > };
>>
>> In this case, the canonical return is "*this". It seems rather pointless
>> to add the const qualifier to an object that you know the caller already
>> has as a non-const reference to, doesn't it?
>>
>> You may think "(t = u).doStuff()" is silly, but since I know that
>> ".doStuff()" operates on the return value of op= (i.e., 't' in this
>> case) I find it rather a great idea. It may not seem so silly in a
>> different context, like this:
>>
>> std::string s;
>> // later
>> s.assign(charPtr).append(otherCharPtr);
>>
>> I often set-up method chains
>> (http://www.parashift.com/c++-faq-lite/references.ht...). It's
>> rather standard in SmallTalk as well (in SmallTalk, if a method doesn't
>> explicitly return something else, it will return self (this).)
>
> Since when method chaining is a good practice in C++?

Since when is it a bad practice? Operator overloading can help us hide
the fact that chaining is taking place but it's one of the prime
examples of when to use it.

Sure, there are times when it is bad style to use chaining, like some of
the cases you have shown, but I prefer consistency over special rules
(i.e. I prefer to always allow chaining, even if it allows some bad
style), than to have some cases where it is not possible).

--
Erik Wikström