Is self assignment test valid?

This is a discussion on Is self assignment test valid? within the c++ forums in Programming Languages category; Martin T. wrote: [...] > EXAMPLE CODE: > class Copyable { > public: > Copyable() > : p_(NULL), > s_(0) > { } > > explicit Copyable(size_t s) > : p_(NULL), > s_(0) > { > p_ = new int[s]; > s_ = s; > } > > ~Copyable() > { > delete p_; Wrong. Correct is: delete[] p_; > } [...] One reason never to use new[]. -- Thomas [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]...

Go Back   Application Development Forum > Programming Languages > c++

Object Mix

Register FAQ Calendar Search Today's Posts Mark Forums Read
Reply

 

LinkBack Thread Tools Display Modes
  #11  
Old 09-05-2008, 12:21 AM
Thomas J. Gritzan
Guest
 
Default Re: Is self assignment test valid?

Martin T. wrote:
[...]
> EXAMPLE CODE:
> class Copyable {
> public:
> Copyable()
> : p_(NULL),
> s_(0)
> { }
>
> explicit Copyable(size_t s)
> : p_(NULL),
> s_(0)
> {
> p_ = new int[s];
> s_ = s;
> }
>
> ~Copyable()
> {
> delete p_;


Wrong. Correct is:

delete[] p_;

> }

[...]

One reason never to use new[].

--
Thomas

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #12  
Old 09-05-2008, 12:21 AM
JoshuaMaurice@gmail.com
Guest
 
Default Re: Is self assignment test valid?

On Sep 4, 3:37 pm, David Abrahams <d...@boostpro.com> wrote:
> on Thu Sep 04 2008, Ulrich Eckhardt <doomster-AT-knuut.de> wrote:
>
> > I guess the alternative that Maciej was thinking about (but never actually
> > mentioned) was the copy and swap approach:

>
> > operator=( T const& other)
> > {
> > T tmp(other); // make a copy
> > swap( *this, tmp);
> > return *this;
> > }

>
> That's the wrong way to write it, though ;-). On real compilers
>
> operator=( T other )
> {
> swap( *this, other);
> return *this;
> }
>
> can be *much* more efficient because of copy elision.
>
> Cheers,
>
> --
> Dave Abrahams
> BoostPro Computinghttp://www.boostpro.com
>
> [ Seehttp://www.gotw.ca/resources/clcm.htmfor info about ]
> [ comp.lang.c++.moderated. First time posters: Do this! ]


I just reviewed the standard to make sure I didn't forget something,
but I'm totally not seeing what you're talking about David Abrahams. I
see exactly copy constructor call in both your example code and in
Ulrich Eckhardt's code. The single copy constructor cannot be elided
because the temporary needs to be created to be swapped with *this.


As an aside, the way I've generally done it is as follows, which I
assume to be equivalent to the two previous examples.

struct T
{
T& operator= (T const& x)
{
T(x).swap(*this);
return *this;
}
void swap(T& x);
};


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #13  
Old 09-05-2008, 05:34 AM
Andrei Alexandrescu
Guest
 
Default Re: Is self assignment test valid?

JoshuaMaurice@gmail.com wrote:
> On Sep 4, 3:37 pm, David Abrahams <d...@boostpro.com> wrote:
>> on Thu Sep 04 2008, Ulrich Eckhardt <doomster-AT-knuut.de> wrote:
>>
>>> I guess the alternative that Maciej was thinking about (but never actually
>>> mentioned) was the copy and swap approach:
>>> operator=( T const& other)
>>> {
>>> T tmp(other); // make a copy
>>> swap( *this, tmp);
>>> return *this;
>>> }

>> That's the wrong way to write it, though ;-). On real compilers
>>
>> operator=( T other )
>> {
>> swap( *this, other);
>> return *this;
>> }
>>
>> can be *much* more efficient because of copy elision.
>>
>> Cheers,


{ quoted signature and banner removed -mod }

> I just reviewed the standard to make sure I didn't forget something,
> but I'm totally not seeing what you're talking about David Abrahams. I
> see exactly copy constructor call in both your example code and in
> Ulrich Eckhardt's code. The single copy constructor cannot be elided
> because the temporary needs to be created to be swapped with *this.
>
>
> As an aside, the way I've generally done it is as follows, which I
> assume to be equivalent to the two previous examples.
>
> struct T
> {
> T& operator= (T const& x)
> {
> T(x).swap(*this);
> return *this;
> }
> void swap(T& x);
> };


That's the politically correct version. It turns out that, when a
function needs to make a copy of its argument, it better takes it by
value in the first place. That way, if an rvalue is passed in, the
compiler can directly fuse the rvalue to the parameter thus saving a copy.

See http://www.erdani.org/publications/cuj-02-2003.html. In short,
again, whenever a function plans to make a copy of its argument, it is
correct and recommendable to just have the function take the argument by
value. That includes the assignment operator. I even managed to convince
Herb to introduce that rather unusual (at that time) recommendation in
the C++ Coding Standards book.

Alas, that rule cannot engulf the copy constructor. I believe that's a
rather gratuitous limitation. Many, many, very many things would have
been simpler had that limitation not been in place, sigh.


Andrei

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #14  
Old 09-06-2008, 12:34 PM
JoshuaMaurice@gmail.com
Guest
 
Default Re: Is self assignment test valid?

On Sep 5, 2:34 am, Andrei Alexandrescu <and...@cs.washington.edu>
wrote:
> JoshuaMaur...@gmail.com wrote:
> > On Sep 4, 3:37 pm, David Abrahams <d...@boostpro.com> wrote:
> >> on Thu Sep 04 2008, Ulrich Eckhardt <doomster-AT-knuut.de> wrote:

>
> >>> I guess the alternative that Maciej was thinking about (but never actually
> >>> mentioned) was the copy and swap approach:
> >>> operator=( T const& other)
> >>> {
> >>> T tmp(other); // make a copy
> >>> swap( *this, tmp);
> >>> return *this;
> >>> }
> >> That's the wrong way to write it, though ;-). On real compilers

>
> >> operator=( T other )
> >> {
> >> swap( *this, other);
> >> return *this;
> >> }

>
> >> can be *much* more efficient because of copy elision.

>
> >> Cheers,

>
> { quoted signature and banner removed -mod }
>
>
>
> > I just reviewed the standard to make sure I didn't forget something,
> > but I'm totally not seeing what you're talking about David Abrahams. I
> > see exactly copy constructor call in both your example code and in
> > Ulrich Eckhardt's code. The single copy constructor cannot be elided
> > because the temporary needs to be created to be swapped with *this.

>
> > As an aside, the way I've generally done it is as follows, which I
> > assume to be equivalent to the two previous examples.

>
> > struct T
> > {
> > T& operator= (T const& x)
> > {
> > T(x).swap(*this);
> > return *this;
> > }
> > void swap(T& x);
> > };

>
> That's the politically correct version. It turns out that, when a
> function needs to make a copy of its argument, it better takes it by
> value in the first place. That way, if an rvalue is passed in, the
> compiler can directly fuse the rvalue to the parameter thus saving a copy.
>
> Seehttp://www.erdani.org/publications/cuj-02-2003.html. In short,
> again, whenever a function plans to make a copy of its argument, it is
> correct and recommendable to just have the function take the argument by
> value. That includes the assignment operator. I even managed to convince
> Herb to introduce that rather unusual (at that time) recommendation in
> the C++ Coding Standards book.


Ok. I see and agree.

> Alas, that rule cannot engulf the copy constructor. I believe that's a
> rather gratuitous limitation. Many, many, very many things would have
> been simpler had that limitation not been in place, sigh.


Now, this makes no sense. What is your alternative, a copy constructor
that takes arguments by value? But how is that argument object
constructed? A copy constructor must take its argument by reference.

There has to be some code somewhere which defines how to copy your
object. That code must take its input by reference as it cannot take
the input by value. That code is the copy constructor.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #15  
Old 09-06-2008, 08:02 PM
Andrei Alexandrescu
Guest
 
Default Re: Is self assignment test valid?

JoshuaMaurice@gmail.com wrote:
> On Sep 5, 2:34 am, Andrei Alexandrescu <and...@cs.washington.edu>
>> Alas, that rule cannot engulf the copy constructor. I believe that's a
>> rather gratuitous limitation. Many, many, very many things would have
>> been simpler had that limitation not been in place, sigh.

>
> Now, this makes no sense. What is your alternative, a copy constructor
> that takes arguments by value? But how is that argument object
> constructed? A copy constructor must take its argument by reference.
>
> There has to be some code somewhere which defines how to copy your
> object. That code must take its input by reference as it cannot take
> the input by value. That code is the copy constructor.


I'm very glad somebody answered, because I think this is an interesting
subject ).

The argument you are making is a form of infinite regression, e.g. if
your copy constructor takes a copy by value, then how do you construct
that copy, and how that that copy construct its argument... ad
infinitum. It is the same as the justification of the original copy
constructor design.

I think the argument is fallacious. The regression does not occur when
an rvalue is at the origin. So the only need is to allow creation of an
rvalue, after which the by-value copy constructor can operate on it.
True, a by-value copy constructor could not copy lvalues, but that is
not a fatal limitation, it is just a useful limitation that the original
design was not aware of. Furthermore, overloading can properly take care
of efficient construction from both lvalues and rvalues.

Does auto_ptr sound familiar? ) Consider a new rule in which by-value
copy constructor is allowed with the semantics that it only accepts
rvalues. Then:

template <class T> class auto_ptr
{
T * p;
public:
auto_ptr(auto_ptr another) { p = another.p; another.p = 0; }
auto_ptr(T * ptr) { p = ptr; }
...
};

Now let's give it a test drive:

auto_ptr<int> a(new int); // fine, copy ctor from int
auto_ptr<int> b = a; // error! nonexisting auto_ptr(auto_ptr&)

In other words, the semantics is quite what auto_ptr tried to attain
with its odd implementation. There are a few simple rules to add, such
as automatically transforming an lvalue into an rvalue upon returning a
local from a function.

Had this rule be in place in the beginning, the entire rvalue reference
proposal would not have been needed. As it turns out, the language has
since made enough turns to make the rule above introduce issues with
existing programs (Howard Hinnant explained me, I forgot the details but
it has something to do with overloading).

Now say you want to define a value type that is initializable with both
rvalues and lvalues. Then you'd define:

struct Val
{
Val(Val rvalue) { ... use the rvalue as you wish ... }
Val(const Val& lvalue) { ... copy lvalue state ... }
...
};

You can also distinguish const lvalues from non-const lvalues if that's
needed, too.


Andrei


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #16  
Old 09-07-2008, 07:28 AM
David Abrahams
Guest
 
Default Re: Is self assignment test valid?


on Sat Sep 06 2008, Andrei Alexandrescu <SeeWebsiteForEmail-AT-erdani.org> wrote:

> Had this rule be in place in the beginning, the entire rvalue reference
> proposal would not have been needed.


Wrong. You still need perfect forwarding.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #17  
Old 09-07-2008, 12:03 PM
Andrei Alexandrescu
Guest
 
Default Re: Is self assignment test valid?

David Abrahams wrote:
> on Sat Sep 06 2008, Andrei Alexandrescu <SeeWebsiteForEmail-AT-erdani.org> wrote:
>
>> Had this rule be in place in the beginning, the entire rvalue reference
>> proposal would not have been needed.

>
> Wrong. You still need perfect forwarding.


That segues into an issue different from copy construction. Namely, the
language design decision causing the lack of perfect forwarding is:
references are not first-class and therefore (naturally) they are not
automatically deduced for lvalues in a template function call.

To clarify:

template <class T> void foo(T a) {}

will only bind T to a non-reference type (one without a trailing "&")
regardless of how it's called. This is a good rule because otherwise
people will get confused that in a call-by-value language they get
pass-by-reference in templates. Yet sometimes there is a need to say,
"if x is an int lvalue bind T = int&. If x is a const int lvalue bind T
to T = const int&. If x is an int rvalue bind T to T = int." (The rvalue
const int case is not necessary in C++ because const is not transitive.)

That particular case could be solved in a number of ways. Assuming that
copy efficiency is taken care of by allowing by-value copy constructors,
the case can be solved without adding a new type by allowing an
"optional reference" as a storage class. For example:

template <class T> void foo(T &? a) {}

indicates that the compiler should generate two foo()s if needed, one
with & and one without.

The remaining problem is, what if foo wants to return one of its
arguments. Here's where auto could come into play to deduce the result
type of the function.

This all would be quite useless "what if" talk, so normally I'd spare
you all of that, but D2 will implement perfect forwarding quite exactly
as outlined above. Which does not make it a much more useful talk to
someone not interested in D, but at least takes the "what if" out ).

To tie this discussion with the one on max, the implementation of max in
D2 would be:

auto max(T1, T2)(ref? T1 a, ref? T2 b) {
return b > a ? b : a;
}


Andrei

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #18  
Old 09-08-2008, 09:36 AM
Andrei Alexandrescu
Guest
 
Default Re: Is self assignment test valid?

Andrei Alexandrescu wrote:
> David Abrahams wrote:
>> on Sat Sep 06 2008, Andrei Alexandrescu
>> <SeeWebsiteForEmail-AT-erdani.org> wrote:
>>
>>> Had this rule be in place in the beginning, the entire rvalue reference
>>> proposal would not have been needed.

>>
>> Wrong. You still need perfect forwarding.

>
> That segues into an issue different from copy construction. Namely, the
> language design decision causing the lack of perfect forwarding is:
> references are not first-class and therefore (naturally) they are not
> automatically deduced for lvalues in a template function call.
>
> To clarify:
>
> template <class T> void foo(T a) {}
>
> will only bind T to a non-reference type (one without a trailing "&")
> regardless of how it's called.


Before someone else points it out: explicit instantiation can force T to
be a reference type. What I meant was that passing lvalues vs. rvalues
won't make a difference to foo when deducing T.

Andrei

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #19  
Old 09-08-2008, 04:05 PM
Niels Dekker - no return address
Guest
 
Default Re: Is self assignment test valid?

Andrei Alexandrescu wrote:
> JoshuaMaurice (at) gm...com wrote:
>> As an aside, the way I've generally done it is as follows, which
>> I assume to be equivalent to the two previous examples.
>>
>> struct T
>> {
>> T& operator= (T const& x)
>> {
>> T(x).swap(*this);
>> return *this;
>> }
>> void swap(T& x);
>> };

>
> That's the politically correct version. It turns out that, when a
> function needs to make a copy of its argument, it better takes it by
> value in the first place. That way, if an rvalue is passed in, the
> compiler can directly fuse the rvalue to the parameter thus saving a
> copy.


Do you think this is still relevant when operator= is inline, and it's just
doing "T(x).swap(*this)"? If so, I might consider submitting another ticket
regarding boost::function, which still copy-assigns in the "politically
correct" way at the moment. As so many of us do :-)

> See http://www.erdani.org/publications/cuj-02-2003.html


Thanks! Looking a the example of passing the result of MakeUrl() to the
function Connect(const String& url), you wrote: "For a compiler to optimize
away the copy, it has to do the Herculean job of (1) getting access to
Connect's definition (hard with separately compiled modules), (2) parse
Connect's definition to develop an understanding of it, and (3) alter
Connect's behavior so that the temporary is fused with finalUrl."

Now is it still such a Herculean job for a compiler to do so for an /inline/
assignment operator that's only just swapping a temporary copy? Honestly, I
haven't done any profiling on this, so I just hope you did so already :-)

Kind regards,

Niels

--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center



--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
  #20  
Old 09-09-2008, 03:28 AM
David Abrahams
Guest
 
Default Re: Is self assignment test valid?


on Mon Sep 08 2008, "Niels Dekker - no return address" <noreply-AT-this.is.invalid> wrote:

> Andrei Alexandrescu wrote:
>> JoshuaMaurice (at) gm...com wrote:
>>> As an aside, the way I've generally done it is as follows, which
>>> I assume to be equivalent to the two previous examples.
>>>
>>> struct T
>>> {
>>> T& operator= (T const& x)
>>> {
>>> T(x).swap(*this);
>>> return *this;
>>> }
>>> void swap(T& x);
>>> };

>>
>> That's the politically correct version. It turns out that, when a
>> function needs to make a copy of its argument, it better takes it by
>> value in the first place. That way, if an rvalue is passed in, the
>> compiler can directly fuse the rvalue to the parameter thus saving a
>> copy.

>
> Do you think this is still relevant when operator= is inline, and it's just
> doing "T(x).swap(*this)"?


Absolutely. The compiler is allowed to elide the implicit copy when the
argument is an rvalue if the corresponding parameter is taken by-value.
However, when you write

T(x)

and x is an lvalue (as it is inside the operator= above), the compiler
is required to honor your explicit instruction to make a copy of x.

> If so, I might consider submitting another ticket regarding
> boost::function, which still copy-assigns in the "politically correct"
> way at the moment. As so many of us do :-)


Could you do that for all the other libraries too? I'm serious, I've
been meaning to make a sweep across the source code to fix this.

>> See http://www.erdani.org/publications/cuj-02-2003.html

>
> Thanks! Looking a the example of passing the result of MakeUrl() to the
> function Connect(const String& url), you wrote: "For a compiler to optimize
> away the copy, it has to do the Herculean job of (1) getting access to
> Connect's definition (hard with separately compiled modules), (2) parse
> Connect's definition to develop an understanding of it, and (3) alter
> Connect's behavior so that the temporary is fused with finalUrl."
>
> Now is it still such a Herculean job for a compiler to do so for an /inline/
> assignment operator that's only just swapping a temporary copy? Honestly, I
> haven't done any profiling on this, so I just hope you did so already :-)


Yes, it's still a fairly herculean job. The compiler would have to
prove that the entire program works exactly the same way with or without
the copy, proving that nothing about the address or identity of the new
T is significant. While it's possible that someone might implement that
optimization for some cases where T's copy constructor, swap, and
destructor are all inlined, that level of understanding is not typically
developed in optimizers.

In the case of copy-elision for by-value arguments and return values,
the compiler is explicitly allowed to _assume_ there is no semantic
difference between the original rvalue and its copy. That's low-hanging
fruit for a compiler writer, that pays huge dividends.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Reply With Quote
Reply


Thread Tools
Display Modes


All times are GMT -5. The time now is 06:57 AM.


Powered by vBulletin® Version 3.7.2
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
vB Ad Management by =RedTyger=

In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.