On 2 Jan., 13:59, Chameleon wrote:
> Στις 01/01/2011 22:02, ο/η SG *γραψε:
> > On 1 Jan., 14:42, Chameleon wrote:
>
> >> * * *A&& *operator+(A&a) {
> >> * * * * *A b;
> >> * * * * *b.str = new char[strlen(this->str) + strlen(a.str) + 1];
> >> * * * * *strcpy(b.str, this->str);
> >> * * * * *strcpy(b.str + strlen(this->str), a.str);
> >> * * * * *cout<< *"A + A = "<< *b.str<< *endl;
> >> * * * * *return std::move(b);
> >> * * *}
>
> > A&& *is a reference type. b is a function-local object. You're
> > returning a reference to a function-local object. This produces a
> > dangling reference. Using this dangling reference invokes undefined
> > behaviour. When in doubt, avoid declaring functions that return rvalue
> > references. This is almost always the wrong thing to do. Your operator
> > + should "return an object by value". Also, if you return a function-
> > local object you should not use std::move as this inhibits a potential
> > copy/move elision.
>
> Thanks a lot!
> After 8 years of C++ programming, I realize that return an object by
> value, doen't invoke the D'tor of the function-local object.
> Whow!!!
That depends. If you return by value and write code like this:
A x = ...;
A y = ...;
A z = x + y;
a good compiler is able to elide two unnecessary copies in the third
line. These unnecessary operations are: 1. Copy-constructing the
return value from the function-local object. 2 Copy-constructing z
from the return value. However, the C++ standard doesn't require these
copy elisions. This is optional and a matter of QoI.
The nice thing with the rvalue reference update is that if the
compiler is not able to elide these unnecessary copies (for whatever
reason), it /has/ to use the move constructor if there is one. What
exactly a "move construction" is depends on how you define your move
constructor. Apart from that, there is no magic involved. So, with
another copiler you might still end up with three distinct objects
(function-local, return value, z) but then the move constructor is
used to construct the latter from the former. The only thing you need
to do if you want to enable move semantics is defining a custom move
constructor (and/or move assignment operator). And sometimes you don't
even need to do that because the compiler will generate appropriate
move operations automatically in some cases. Actually, you should
adopt a class design style that minimizes the need for user-defined
copy/move operations.
> So, the "&&" needed in case where you must put an object in a
> vector<UberObjectClass>. Until now, I used vector<UberObjectClass*>.
> Am I right??
Sorry, I don't understand this question. I think you're saying that
with rvalue references and move semantics we are now able to put
"heavy objects" directly into containers instead of storing pointers.
If so, you're right as long as you ÜberObjectClass has a cheap move
constructor. The number of copy/move constructions can be further
reduced by using the new emplace functions (emplace, emplace_back,
etc...).
Cheers!
SG
|