Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Can a temporary be assigned to itself?

100 views
Skip to first unread message

Juha Nieminen

unread,
Mar 22, 2013, 5:05:54 AM3/22/13
to
If you are implementing the regular operator=() for a class, you (usually)
need to take into account the special case that the same object is being
assigned to itself. (If you don't, you may end up deleting the managed
resource when you shouldn't.)

However, is the same true for the operator=() taking an rvalue reference?
Do you have to take into account the possibility that the object may be
assigned to itself?


--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Öö Tiib

unread,
Mar 22, 2013, 5:37:44 AM3/22/13
to
On Friday, 22 March 2013 11:05:54 UTC+2, Juha Nieminen wrote:
> If you are implementing the regular operator=() for a class, you (usually)
> need to take into account the special case that the same object is being
> assigned to itself. (If you don't, you may end up deleting the managed
> resource when you shouldn't.)

The other reason is that self-assignment (while not illegal) is typo defect on
most cases. Sometimes it is worth trying to achieve some run-time diagnostic
in debug builds.

Typical implementation is copy-construct local and swap with it. That will
work on case of self assignment.

> However, is the same true for the operator=() taking an rvalue reference?
> Do you have to take into account the possibility that the object may be
> assigned to itself?

I do not think so. Even if someone can conjure up a trick, it does not feel
to be reasonable situation.

Balog Pal

unread,
Mar 22, 2013, 8:30:55 AM3/22/13
to
On 3/22/2013 10:37 AM, �� Tiib wrote:
> On Friday, 22 March 2013 11:05:54 UTC+2, Juha Nieminen wrote:
>> If you are implementing the regular operator=() for a class, you (usually)
>> need to take into account the special case that the same object is being
>> assigned to itself. (If you don't, you may end up deleting the managed
>> resource when you shouldn't.)
>
> The other reason is that self-assignment (while not illegal) is typo defect on
> most cases.

Is it really? Sure, a=a makes little sense, but that hardly survives
review. The normal case is like a = foo() or vec[i] = vec[foo(i))].

> Sometimes it is worth trying to achieve some run-time diagnostic
> in debug builds.

I never thought of that.
Btw the more serious case for self-assignment and aliasing are not the
plain but the compound assignments. where the straight form a+=a makes
perfect sense too.

>> However, is the same true for the operator=() taking an rvalue reference?
>> Do you have to take into account the possibility that the object may be
>> assigned to itself?
>
> I do not think so. Even if someone can conjure up a trick, it does not feel
> to be reasonable situation.

Yeah, IMO it would need some pretty evil trickery that is a more serious
issue in itself.



James Kanze

unread,
Mar 22, 2013, 9:41:22 AM3/22/13
to
On Friday, 22 March 2013 09:05:54 UTC, Juha Nieminen wrote:

> If you are implementing the regular operator=() for a class, you (usually)
> need to take into account the special case that the same object is being
> assigned to itself. (If you don't, you may end up deleting the managed
> resource when you shouldn't.)

If you need to take the special case of self-assignment into
account, your operator is probably broken already. Think about
what might happen if you delete something in the object, and
then the acquisition of your resource throws. You end up with
an object with a pointer to a deleted resource.

--
James

Öö Tiib

unread,
Mar 22, 2013, 10:10:39 AM3/22/13
to
On Friday, 22 March 2013 14:30:55 UTC+2, Balog Pal wrote:
> On 3/22/2013 10:37 AM, �� Tiib wrote:
> > On Friday, 22 March 2013 11:05:54 UTC+2, Juha Nieminen wrote:
> >> If you are implementing the regular operator=() for a class, you (usually)
> >> need to take into account the special case that the same object is being
> >> assigned to itself. (If you don't, you may end up deleting the managed
> >> resource when you shouldn't.)
> >
> > The other reason is that self-assignment (while not illegal) is typo
> > defect on most cases.
>
> Is it really? Sure, a=a makes little sense, but that hardly survives
> review. The normal case is like a = foo() or vec[i] = vec[foo(i))].

If this has high likelihood to end with self-assignment then there might be
is some higher level logic error. For me self-assignment is legal thing
that should happen very rarely and on most cases is worth attention.

Same is with 'swap(x,x)' ... it is often worth attention why it did happen.

> > Sometimes it is worth trying to achieve some run-time diagnostic
> > in debug builds.
>
> I never thought of that.

When you do not care then it makes sense to write something very simple,
like that:

Foo& Foo::operator=(Foo rhs)
{
swap(rhs);
return *this;
}

All ways to worry about self assignment erased.

> Btw the more serious case for self-assignment and aliasing are not the
> plain but the compound assignments. where the straight form a+=a makes
> perfect sense too.

Yes, but that is totally different function. Usually operator+(2) is implemented in terms of it.

> >> However, is the same true for the operator=() taking an rvalue reference?
> >> Do you have to take into account the possibility that the object may be
> >> assigned to itself?
> >
> > I do not think so. Even if someone can conjure up a trick, it does not feel
> > to be reasonable situation.
>
> Yeah, IMO it would need some pretty evil trickery that is a more serious
> issue in itself.

Also now I checked my code and seems that I have none assignments that
need a self assignment check for other purposes than for the debug
diagnostic. Those just somehow work without that check on case of a=a too.
It is perhaps gone so because I was achieving exception safety.

The shortest such trickery is perhaps ...

x = std::move(x);

Juha Nieminen

unread,
Mar 22, 2013, 11:07:38 AM3/22/13
to
James Kanze <james...@gmail.com> wrote:
>> If you are implementing the regular operator=() for a class, you (usually)
>> need to take into account the special case that the same object is being
>> assigned to itself. (If you don't, you may end up deleting the managed
>> resource when you shouldn't.)
>
> If you need to take the special case of self-assignment into
> account, your operator is probably broken already.

Thanks for the non-answer.

That's not what I was asking.

Howard Hinnant

unread,
Mar 22, 2013, 12:22:09 PM3/22/13
to
On Mar 22, 5:05 am, Juha Nieminen <nos...@thanks.invalid> wrote:
> If you are implementing the regular operator=() for a class, you (usually)
> need to take into account the special case that the same object is being
> assigned to itself. (If you don't, you may end up deleting the managed
> resource when you shouldn't.)
>
> However, is the same true for the operator=() taking an rvalue reference?
> Do you have to take into account the possibility that the object may be
> assigned to itself?

An object can be self-move-assigned. The operation does not need to
be a no-op, like the self-copy-assignment case. It simply needs to
not corrupt the object.


#include <iostream>
#include <algorithm>

class A
{
int* data_;
public:
A(int data = 0) : data_(new int(data)) {}
~A() {delete data_;}
A(A&& a) : data_(a.data_) {a.data_ = nullptr;}
A& operator=(A&& a)
{
// dump existing resources
delete data_;
// put lhs in valid state
data_ = nullptr;
// Move resources
data_ = a.data_;
// put rhs in valid state
a.data_ = nullptr;
return *this;
}

friend std::ostream&
operator<<(std::ostream& os, const A& a)
{
if (a.data_ != nullptr)
os << "A = " << *a.data_;
else
os << "A = null";
return os;
}
};

int main()
{
A a1(1);
std::cout << a1 << '\n';
std::cout << "self swap:\n";
std::swap(a1, a1);
std::cout << a1 << '\n';
std::cout << "self move assignment:\n";
a1 = std::move(a1);
std::cout << a1 << '\n';
}

A = 1
self swap:
A = 1
self move assignment:
A = null

Öö Tiib

unread,
Mar 22, 2013, 12:59:31 PM3/22/13
to
On Friday, 22 March 2013 18:22:09 UTC+2, Howard Hinnant wrote:
> On Mar 22, 5:05 am, Juha Nieminen <nos...@thanks.invalid> wrote:
> > If you are implementing the regular operator=() for a class, you (usually)
> > need to take into account the special case that the same object is being
> > assigned to itself. (If you don't, you may end up deleting the managed
> > resource when you shouldn't.)
> >
> > However, is the same true for the operator=() taking an rvalue reference?
> > Do you have to take into account the possibility that the object may be
> > assigned to itself?
>
> An object can be self-move-assigned. The operation does not need to
> be a no-op, like the self-copy-assignment case. It simply needs to
> not corrupt the object.

...

> A = 1
>
> self swap:
>
> A = 1
>
> self move assignment:
>
> A = null

Isn't that quite annoying to some? Legally fine but I suspect that I would
love a guaranteed crash during self-assignment slightly more.

Howard Hinnant

unread,
Mar 22, 2013, 1:10:06 PM3/22/13
to
On Mar 22, 12:59 pm, Öö Tiib <oot...@hot.ee> wrote:
>
> Isn't that quite annoying to some? Legally fine but I suspect that I would
> love a guaranteed crash during self-assignment slightly more.

I have sometimes suggested that people put assert(this != &other) in
their move assignment operators to actively detect self-move-
assignment and treat it as a performance bug.

If you do that, and you detect that is code I wrote that is causing
you to self-move-assign, I would appreciate a bug report and I will
get it fixed. That being said, if it is your code that is doing a
self-swap, causing std::swap to self-move-assign, I will push it back
to you to fix the self-swap. :-)

PS: "You" here refers to everyone that might be a client of code I
write, e.g. libc++.

Öö Tiib

unread,
Mar 22, 2013, 2:29:53 PM3/22/13
to
On Friday, 22 March 2013 19:10:06 UTC+2, Howard Hinnant wrote:
> On Mar 22, 12:59 pm, Öö Tiib <oot...@hot.ee> wrote:
> > Isn't that quite annoying to some? Legally fine but I suspect that I would
> > love a guaranteed crash during self-assignment slightly more.
>
> I have sometimes suggested that people put assert(this != &other) in
> their move assignment operators to actively detect self-move-
> assignment and treat it as a performance bug.

I prefer to log/trace it in debug builds. Performance defects do
not deserve harsher punishments.

> If you do that, and you detect that is code I wrote that is causing
> you to self-move-assign, I would appreciate a bug report and I will
> get it fixed. That being said, if it is your code that is doing a
> self-swap, causing std::swap to self-move-assign, I will push it back
> to you to fix the self-swap. :-)

On the case with swap ... "Effects: Exchanges values stored in two
locations." That implicitly means that the effect is undefined if the
values are not in two locations and so I feel that I have no true right
to complain, only to pout a bit. :-/

> PS: "You" here refers to everyone that might be a client of code I
> write, e.g. libc++.

Yes! You are in admirable situation. I trust that your 'swap(a,a)'
actually does work nicely and the effect is what the intuition tells
not the horrors what happen when standard "does not tell". ;-)

Balog Pal

unread,
Mar 22, 2013, 2:46:59 PM3/22/13
to
I heard that claim a lot, and it sounded good, but I never could really
map it to actual code.

Taking your example, my code would call Dispose() that will call the
proper delete and set the pointer to NULL, then we proceed to allocation
part. Should it fail we're in a proper non-owning state fulfilling the
basic XG.

Sure it it possible to do things in improper order or overlook
something, but that applies to anything in programming. I considered the
suggested alternatives many times then concluded that the (pretty rare)
instances I have providing op= are okay with the self-assignment check
up front and the rest is fine.

IME most classes will have either default or deleted op=, the rest is
maybe 1%. And that small population is mostly primitive library carried
over for decades.

In the cases I reviewed with broken op= or more general rule-of-three
related discrepancy the problem was not with self-assigment or its
strategy but that the actual class should not have t in the first place,
rather pick up a proper member or base class. Well, it coming back now
-- the problem was usually with obtaining something in ctor body, and
leaked due to dtor not called. And fixing that the proper way, using a
proper controller for the resource made the op= gone for good as bonus
side effect. :)



Öö Tiib

unread,
Mar 22, 2013, 3:52:44 PM3/22/13
to
On Friday, 22 March 2013 20:46:59 UTC+2, Balog Pal wrote:
> On 3/22/2013 2:41 PM, James Kanze wrote:
> > On Friday, 22 March 2013 09:05:54 UTC, Juha Nieminen wrote:
> >
> >> If you are implementing the regular operator=() for a class, you (usually)
> >> need to take into account the special case that the same object is being
> >> assigned to itself. (If you don't, you may end up deleting the managed
> >> resource when you shouldn't.)
> >
> > If you need to take the special case of self-assignment into
> > account, your operator is probably broken already. Think about
> > what might happen if you delete something in the object, and
> > then the acquisition of your resource throws. You end up with
> > an object with a pointer to a deleted resource.
>
> I heard that claim a lot, and it sounded good, but I never could really
> map it to actual code.

Object should remain in sane state if anything throws exceptions
during assignment. Sole imaginable sane state is "like it was before".

On my case it seems that achieving that has also achieved that the
self-assignment checks are not needed anymore.

[...]

> IME most classes will have either default or deleted op=, the rest is
> maybe 1%. And that small population is mostly primitive library carried
> over for decades.

Interesting.

Domains and applications certainly differ. IME most classes are not
polymorphic. It is efficient to put most such things into vector
(or deque) or used as direct data members of other non-polymorphic
classes.

Lot of non-polymorphic classes may have constant or reference or
unique_ptr or auto_ptr or boost::scoped_ptr or naked pointer
non-static members. These classes should be made explicitly copyable
or movable if one wants them to be in vector or deque or the like.

James Kanze

unread,
Mar 22, 2013, 4:11:10 PM3/22/13
to
On Friday, 22 March 2013 18:46:59 UTC, Balog Pal wrote:
> On 3/22/2013 2:41 PM, James Kanze wrote:
> > On Friday, 22 March 2013 09:05:54 UTC, Juha Nieminen wrote:

> >> If you are implementing the regular operator=() for a class, you (usually)
> >> need to take into account the special case that the same object is being
> >> assigned to itself. (If you don't, you may end up deleting the managed
> >> resource when you shouldn't.)

> > If you need to take the special case of self-assignment into
> > account, your operator is probably broken already. Think about
> > what might happen if you delete something in the object, and
> > then the acquisition of your resource throws. You end up with
> > an object with a pointer to a deleted resource.

> I heard that claim a lot, and it sounded good, but I never could really
> map it to actual code.

> Taking your example, my code would call Dispose() that will call the
> proper delete and set the pointer to NULL, then we proceed to allocation
> part. Should it fail we're in a proper non-owning state fulfilling the
> basic XG.

That's doing things the hard way. As a general rule, you should
construct all of the new values before you start tearing down
anything.

--
James

Balog Pal

unread,
Mar 22, 2013, 4:24:59 PM3/22/13
to
On 3/22/2013 8:52 PM, �� Tiib wrote:

> Object should remain in sane state if anything throws exceptions
> during assignment. Sole imaginable sane state is "like it was before".

That is strong XG, can be done too at about the same amount of code but
some increased resource cost at runtime. I don't agree that strong XG is
the only sane way but that is up to opinion in general -- and detailed
design at a particular instance, so we should not debate it. :)
...
> Lot of non-polymorphic classes may have constant or reference or
> unique_ptr or auto_ptr or boost::scoped_ptr or naked pointer
> non-static members. These classes should be made explicitly copyable
> or movable if one wants them to be in vector or deque or the like.

I have a full suite of smart pointers: NC, DC, transfer and shared,
certainly if I want a copyable object I use a the DC variant over
anything else. IMO same applies to any resource handler. (btw the said
pointers are policy-based so it's easy to use them for any kind of
resource; I recall a few times using fake-copy that was okay for the
case).

IME starting to hand-craft an op= is the last resort and is a ticked for
more easy-to-break maintenance too.



Balog Pal

unread,
Mar 22, 2013, 4:51:42 PM3/22/13
to
On 3/22/2013 9:11 PM, James Kanze wrote:
>> Taking your example, my code would call Dispose() that will call the
>> proper delete and set the pointer to NULL, then we proceed to allocation
>> part. Should it fail we're in a proper non-owning state fulfilling the
>> basic XG.
>
> That's doing things the hard way. As a general rule, you should
> construct all of the new values before you start tearing down
> anything.

That's also a fair approach if having both new and old around is not
considered risk for resource overuse.

IMO that general rule applies to complex situations, mostly in the
application realm -- way less in the basic primitives that just manage a
single resource or memory block.

When I say

String a("a");
a = "ab";
and the latter fails with memory error I honestly see no value to make
it strong XG, and keep original value.

I tried to figure out what std::string is supposed to do here but choked
;-)

But my point is not really the how, but that it IMO it can be reasonably
expected to be done correctly in those rare cases, and if this is a
problem for someone I can't imagine how he can deal with something less
trivial.

88888 Dihedral

unread,
Mar 22, 2013, 4:57:32 PM3/22/13
to
Öö Tiib於 2013年3月23日星期六UTC+8上午12時59分31秒寫道:
Well, I give a simple example used in c/c++ to illustrate
the unsafe part of c/c++.

int mcopy( char *p, char *q, int n)

{
//
if n==0 return 0; // nothing to do
if n<0 return -1; // failed
// if p and q are overlapped what will happen ???
// caller responsible version

while(n--) {*p++=*q++;}
return 0; // OK
}

Öö Tiib

unread,
Mar 23, 2013, 7:24:51 AM3/23/13
to
On Friday, 22 March 2013 22:24:59 UTC+2, Balog Pal wrote:
> On 3/22/2013 8:52 PM, Öö Tiib wrote:
> > Object should remain in sane state if anything throws exceptions
> > during assignment. Sole imaginable sane state is "like it was before".
>
> That is strong XG, can be done too at about the same amount of code but
> some increased resource cost at runtime. I don't agree that strong XG is
> the only sane way but that is up to opinion in general -- and detailed
> design at a particular instance, so we should not debate it. :)

I did not want to debate it. I have had bad experience with assignment
damaging things. Since '=' is so innocent in code I feel it should be
robust. Common case ... some GUI designer made it too permissive
and user can do illegal copy or move. Throwing is fine since it is
defect of GUI ... however being broken afterwards is out of question.

Strong exception safety guarantee is not needed in lot of other cases.

> > Lot of non-polymorphic classes may have constant or reference or
> > unique_ptr or auto_ptr or boost::scoped_ptr or naked pointer
> > non-static members. These classes should be made explicitly copyable
> > or movable if one wants them to be in vector or deque or the like.
>
> I have a full suite of smart pointers: NC, DC, transfer and shared,
> certainly if I want a copyable object I use a the DC variant over
> anything else.

Reference or constant members you avoid? shared_ptr member is quite
common. Sometimes deep copy sometimes shared copy is needed
when copying an object with such member. Your DC pointer is
interesting. Does it have non-owning side-kick or do you use raw pointer
instead? Transfer and non-copyable ... isn't it unique_ptr?

> IMO same applies to any resource handler. (btw the said
> pointers are policy-based so it's easy to use them for any kind of
> resource; I recall a few times using fake-copy that was okay for the
> case).

I avoid allowing assignment or copying of resources like threads, files or
sockets with just '=' character ... being laconic here is hiding the cost
tag that certainly matters. Moving is better since it is cheaper.

> IME starting to hand-craft an op= is the last resort and is a ticked for
> more easy-to-break maintenance too.

It is important to make novice to realize that compiler will create the
things itself if it only can and that compiler does not always
understand his intentions. If novice does not know that issue with
classes in C++ then he is doomed to write broken classes. Everybody
have seen copyable singletons I trust? Hiding that part of C++ does
not work it feels.

Note that implicit assignment operators do not even offer the
basic exception safety guarantee on lot of cases. So either everything
is nothrow during assignment or you *have* to hand-craft them.

88888 Dihedral

unread,
Mar 23, 2013, 8:32:28 PM3/23/13
to
Öö Tiib於 2013年3月23日星期六UTC+8下午7時24分51秒寫道:
Are you building some class families which do operator
overloading without carefully investigating the closure
problem of any operation result involved?


Balog Pal

unread,
Mar 24, 2013, 7:44:19 AM3/24/13
to
On 3/23/2013 12:24 PM, �� Tiib wrote:
> I have had bad experience with assignment
> damaging things. Since '=' is so innocent in code I feel it should be
> robust.

I can understand that. In the early days I also had many problems
related to op=. Then we learned the rule of three. Then we learned RAII,
and sticking to that resulted elimination of hand-crafted op= and cctor
from user code. From there I recall no problems worth mentioning.

If I see an op= in code, my first reaction is to ask a ton of questions
why is it there, and likely result is to send the whole class to design
table. For good. :) In fewer cases it is just cut with slight
modifications. In the remaining rare cases it gets a very thorough
review considering all kind of scenarions, including self-assignment and
exceptions at any point.

>> I have a full suite of smart pointers: NC, DC, transfer and shared,
>> certainly if I want a copyable object I use a the DC variant over
>> anything else.
>
> Reference or constant members you avoid?

I'm not avoiding them, but they're pretty rare turn turn up. And I can't
recall a single case they would trigger special ops -- the natural
consequence is a sensible default cctor and a deleted op=, that is fine
for me. If I wrote them by hand the copy would not be "equivalent" to
the original, and I guess that would surprise someone later on.

> shared_ptr member is quite common.

Not in my work. But I see no problem with it -- if it's used for
sharing, then it will apply for its container just by doing nothing. (Or
the container deletes the copy possibility and just uses it as a NC
holder for other conveniences -- my NC pointer uses CHECKED_DELETE,
shared_ptr is smarter than that.)

> Sometimes deep copy sometimes shared copy is needed
> when copying an object with such member.

IMO it's rarther either shared or NC. If I want DC, why pick the
shared_ptr over DC in the first place?

> Your DC pointer is
> interesting. Does it have non-owning side-kick or do you use raw pointer
> instead? Transfer and non-copyable ... isn't it unique_ptr?

'Transfer' is the plain old auto_ptr, replaced by unique_ptr these days.
NC would have been boost::scoped_ptr, but it turned out to have crippled
interface, so I made mine using auto_ptr's interface (I like the
reset/release a lot, and need get regularly dealing with C-interfaced
other components), just stripped cctor, op=, the templated ctors (mostly
out of fear) and the deleter comes from policy. In some environments it
got an extra member to allow external init (do reset() and provide &ptr
to be assigned) where such approach to create new objects is common.

>> IMO same applies to any resource handler. (btw the said
>> pointers are policy-based so it's easy to use them for any kind of
>> resource; I recall a few times using fake-copy that was okay for the
>> case).
>
> I avoid allowing assignment or copying of resources like threads, files or
> sockets with just '=' character ... being laconic here is hiding the cost
> tag that certainly matters. Moving is better since it is cheaper.

I used all of those only for NC. Certainly move would make sense, but
the compilers I use did not yet pick up move semantics properly,
eventually I'll switch to more of that.

>> IME starting to hand-craft an op= is the last resort and is a ticked for
>> more easy-to-break maintenance too.
>
> It is important to make novice to realize that compiler will create the
> things itself if it only can and that compiler does not always
> understand his intentions.

Rule of 3 covers that, and certainly a novice shall never gain check-in
rights before mastering that (along with rest of EC++3rd)...

> If novice does not know that issue with
> classes in C++ then he is doomed to write broken classes. Everybody
> have seen copyable singletons I trust? Hiding that part of C++ does
> not work it feels.

... but knowing those details do not map directly to design and coding,
there's way more. And broken classes are supposed to be caught on
review. sure, novices are doomed to write a ton of them before gaining
wisdom to get it right on the first attempt. and with strict meaning of
'broken' I guess even veterans fail with the first attempt more often
than not. ;-)))

IRL we fight program complexity on a broader level, that usually imposes
restrictions on how objects are created, held, disposed of; implicit
responsibilities for some actions, forbidding some other activity (like
storing pointers/refs to a thing you can't prove to live long enough).

> Note that implicit assignment operators do not even offer the
> basic exception safety guarantee on lot of cases.

They do memberwise assignment, that I recall was fine for all my value
classes. I try to imagine a broken situation, but the only scenario
jumps to mind is when I have a group of members that shall be in synch
and one may throw on copy -- my first question would be "how come that
group is not a class in its own right?"

> So either everything
> is nothrow during assignment or you *have* to hand-craft them.

IMO we're lightyears from that.


Öö Tiib

unread,
Mar 24, 2013, 9:58:05 AM3/24/13
to
On Sunday, 24 March 2013 13:44:19 UTC+2, Balog Pal wrote:
> On 3/23/2013 12:24 PM, Öö Tiib wrote:
> > Reference or constant members you avoid?
>
> I'm not avoiding them, but they're pretty rare turn turn up. And I can't
> recall a single case they would trigger special ops -- the natural
> consequence is a sensible default cctor and a deleted op=, that is fine
> for me. If I wrote them by hand the copy would not be "equivalent" to
> the original, and I guess that would surprise someone later on.

Yes, copyability is less common than language designer originally
thought IMO ... but movability is more usual. Constants are quite
common members. It is quite frequently suggested to use immutability
wherever it makes sense.

Reference is very close to constant not-null pointer. I often have
references in components to track relation back to composite. If
moving components around makes sense (not often) then there is
assignment needed. Such assignment can assert that the composite
references are already equal or throw logic_error if they aren't
because assigning components of different composite does not make
sense.

...
> > Sometimes deep copy sometimes shared copy is needed
> > when copying an object with such member.
>
> IMO it's rarther either shared or NC. If I want DC, why pick the
> shared_ptr over DC in the first place?

Because shared_ptr has handy non-owning (but tracking) partner. I need to
think how to make similar thing to deep copy pointer. I already asked it:

> > Your DC pointer is
> > interesting. Does it have non-owning side-kick or do you use raw pointer
> > instead?

...
> IRL we fight program complexity on a broader level, that usually imposes
> restrictions on how objects are created, held, disposed of; implicit
> responsibilities for some actions, forbidding some other activity (like
> storing pointers/refs to a thing you can't prove to live long enough).

An object that stores pointer has to have ability to track the lifetime
of pointed at object, simplest case is when the life time of two objects
is bound (either way), reference count is powerful way to track, as are
various signaling and observing systems.

> > Note that implicit assignment operators do not even offer the
> > basic exception safety guarantee on lot of cases.
>
> They do memberwise assignment, that I recall was fine for all my value
> classes. I try to imagine a broken situation, but the only scenario
> jumps to mind is when I have a group of members that shall be in synch
> and one may throw on copy -- my first question would be "how come that
> group is not a class in its own right?"

It is. It is such "group" whose members are interrelated that we are
usually talking about when talking about classes. Lot of combinations of
values of members of classes are usually invalid. So now we talk
about copy-assignment and move-assignment of such groups. Half assignment
can end with invalid combination.

> > So either everything
> > is nothrow during assignment or you *have* to hand-craft them.
>
> IMO we're lightyears from that.

We are ligtyears from everything being nothrow during copy-assignment,
with move-assignment we are commonly there.

James Kanze

unread,
Mar 24, 2013, 10:45:58 AM3/24/13
to
On Saturday, March 23, 2013 11:24:51 AM UTC, Öö Tiib wrote:
> On Friday, 22 March 2013 22:24:59 UTC+2, Balog Pal wrote:
> > On 3/22/2013 8:52 PM, Öö Tiib wrote:
> > > Object should remain in sane state if anything throws exceptions
> > > during assignment. Sole imaginable sane state is "like it was before".

> > That is strong XG, can be done too at about the same amount of code but
> > some increased resource cost at runtime. I don't agree that strong XG is
> > the only sane way but that is up to opinion in general -- and detailed
> > design at a particular instance, so we should not debate it. :)

> I did not want to debate it.

There's no reason to debate. It depends on the application.
I've worked a lot on large servers; in this case, there are two
categories of objects: those which have been created in the
context of the request, and "permanent" obejcts, which will be
accessible in future requests. For the first, the only
guarantee which is necessary is that they can be destructed.
The second require the strong guarantee. (Although one
possibility, which I often use, is to make copies of these
objects, and modify them, swapping them in in the commit phase.)

> I have had bad experience with assignment
> damaging things.

There's damage, and there's damage. I think every one would
agree that leaving an object in a state where it cannot be
destructed is unacceptable. Always. For the rest, all that is
required for temporary objects, created in the context of a
transaction, is that they can be destructed.

> Since '=' is so innocent in code I feel it should be
> robust. Common case ... some GUI designer made it too permissive
> and user can do illegal copy or move. Throwing is fine since it is
> defect of GUI ... however being broken afterwards is out of question.

For most objects where it is important, the obvious solution is
to not support assignment. In my experience (which does *not*
include all application domains), all that is needed for objects
which support assignment is that the results can be destructed
(the weakest acceptable guarantee). But in my experience, there
is a large category of objects which don't support assignment.

--
James

James Kanze

unread,
Mar 24, 2013, 10:55:25 AM3/24/13
to
On Friday, March 22, 2013 8:51:42 PM UTC, Balog Pal wrote:
> On 3/22/2013 9:11 PM, James Kanze wrote:
> >> Taking your example, my code would call Dispose() that will call the
> >> proper delete and set the pointer to NULL, then we proceed to allocation
> >> part. Should it fail we're in a proper non-owning state fulfilling the
> >> basic XG.

> > That's doing things the hard way. As a general rule, you should
> > construct all of the new values before you start tearing down
> > anything.

> That's also a fair approach if having both new and old around is not
> considered risk for resource overuse.

That could be a consideration in certain cases. Never the less,
if you're working in a transaction system, at some point, you do
need to be able to restore the original version. Unless memory
issues are a concern, the simplest solution involves saving a
copy of the original value. (I've never had to implement this
in contexts where memory was an issue, so I can't comment on
this.)

> IMO that general rule applies to complex situations, mostly in the
> application realm -- way less in the basic primitives that just manage a
> single resource or memory block.

> When I say

> String a("a");
> a = "ab";
> and the latter fails with memory error I honestly see no value to make
> it strong XG, and keep original value.

Totally agreed. All that is required (IMHO) is that a be
destructable.

> I tried to figure out what std::string is supposed to do here but choked
> ;-)

I wouldn't swear to it, but I think that the standard requires
the strong guarantee here: that if there is an exception in the
assignment, that the value of a be unchanged. (In the case of
std::string, this is relatively easy to implement.) In
practice, of course, I find it difficult to imagine a case where
more is needed than that a be destructible.

--
James

Öö Tiib

unread,
Mar 24, 2013, 11:33:40 AM3/24/13
to
On Sunday, 24 March 2013 16:45:58 UTC+2, James Kanze wrote:
> On Saturday, March 23, 2013 11:24:51 AM UTC, Öö Tiib wrote:
> > On Friday, 22 March 2013 22:24:59 UTC+2, Balog Pal wrote:
> > > On 3/22/2013 8:52 PM, Öö Tiib wrote:
> > > > Object should remain in sane state if anything throws exceptions
> > > > during assignment. Sole imaginable sane state is "like it was before".
>
> > > That is strong XG, can be done too at about the same amount of code but
> > > some increased resource cost at runtime. I don't agree that strong XG is
> > > the only sane way but that is up to opinion in general -- and detailed
> > > design at a particular instance, so we should not debate it. :)
>
> > I did not want to debate it.
>
> There's no reason to debate. It depends on the application.
> I've worked a lot on large servers; in this case, there are two
> categories of objects: those which have been created in the
> context of the request, and "permanent" obejcts, which will be
> accessible in future requests. For the first, the only
> guarantee which is necessary is that they can be destructed.
> The second require the strong guarantee.

Interesting concept. How do you make difference between classes that may
be used for temporary objects during transaction and classes that have
used for more permanent objects? Do you have some special trait?

> (Although one
> possibility, which I often use, is to make copies of these
> objects, and modify them, swapping them in in the commit phase.)

Yes, such system-wide idiom lets to enwiden usefulness of weak quarantee.
The problem is that ... some stuff possibly affected ... is rather
expensive to clone (just for case).

> > I have had bad experience with assignment
> > damaging things.
>
> There's damage, and there's damage. I think every one would
> agree that leaving an object in a state where it cannot be
> destructed is unacceptable. Always. For the rest, all that is
> required for temporary objects, created in the context of a
> transaction, is that they can be destructed.
>
> > Since '=' is so innocent in code I feel it should be
> > robust. Common case ... some GUI designer made it too permissive
> > and user can do illegal copy or move. Throwing is fine since it is
> > defect of GUI ... however being broken afterwards is out of question.
>
> For most objects where it is important, the obvious solution is
> to not support assignment. In my experience (which does *not*
> include all application domains), all that is needed for objects
> which support assignment is that the results can be destructed
> (the weakest acceptable guarantee). But in my experience, there
> is a large category of objects which don't support assignment.

I meant case where user can copy-paste or cut-paste. It is so common
that everybody know the accelerators (ctrl-x,ctrl-c,ctrl-v) and mouse
gestures (drag-drop, ctrl-drag-drop) to those. I often use far more sophisticated features (like serializing-unserialising) at that spot but
on lot of cases it is in essence copy or move exposed in GUI.

88888 Dihedral

unread,
Mar 24, 2013, 12:54:50 PM3/24/13
to
Öö Tiib於 2013年3月24日星期日UTC+8下午11時33分40秒寫道:
A lot programmers are exploring more subtle
problems in true distributed programming languages such as
Erlang.

James Kanze

unread,
Mar 24, 2013, 3:26:22 PM3/24/13
to
On Sunday, March 24, 2013 3:33:40 PM UTC, Öö Tiib wrote:
> On Sunday, 24 March 2013 16:45:58 UTC+2, James Kanze wrote:
> > On Saturday, March 23, 2013 11:24:51 AM UTC, Öö Tiib wrote:
> > > On Friday, 22 March 2013 22:24:59 UTC+2, Balog Pal wrote:
> > > > On 3/22/2013 8:52 PM, Öö Tiib wrote:
> > > > > Object should remain in sane state if anything throws exceptions
> > > > > during assignment. Sole imaginable sane state is "like it was before".

> > > > That is strong XG, can be done too at about the same amount of code but
> > > > some increased resource cost at runtime. I don't agree that strong XG is
> > > > the only sane way but that is up to opinion in general -- and detailed
> > > > design at a particular instance, so we should not debate it. :)

> > > I did not want to debate it.

> > There's no reason to debate. It depends on the application.
> > I've worked a lot on large servers; in this case, there are two
> > categories of objects: those which have been created in the
> > context of the request, and "permanent" obejcts, which will be
> > accessible in future requests. For the first, the only
> > guarantee which is necessary is that they can be destructed.
> > The second require the strong guarantee.

> Interesting concept. How do you make difference between classes that may
> be used for temporary objects during transaction and classes that have
> used for more permanent objects? Do you have some special trait?

You design your application. Classes are designed to fulfill a
specific role in the application. That pretty much determines
the use they will have.

> > (Although one
> > possibility, which I often use, is to make copies of these
> > objects, and modify them, swapping them in in the commit phase.)

> Yes, such system-wide idiom lets to enwiden usefulness of weak quarantee.
> The problem is that ... some stuff possibly affected ... is rather
> expensive to clone (just for case).

Such system-wide idioms are part of the requirements
specification, at least in servers. You *must* implement
rollback somehow. To my knowledge, there are only two
possibilities: cloning the object, or providing some sort of
"undo" facility. An undo facility is significantly more
complex. And realistically, for a large server, only a small
percentage of the objects will be involved in any single
transaction, so the impact on memory usage can't really be
significant. (There are probably exceptions, but I've not seen
them in practice.)

[...]
> > > Since '=' is so innocent in code I feel it should be
> > > robust. Common case ... some GUI designer made it too permissive
> > > and user can do illegal copy or move. Throwing is fine since it is
> > > defect of GUI ... however being broken afterwards is out of question.

> > For most objects where it is important, the obvious solution is
> > to not support assignment. In my experience (which does *not*
> > include all application domains), all that is needed for objects
> > which support assignment is that the results can be destructed
> > (the weakest acceptable guarantee). But in my experience, there
> > is a large category of objects which don't support assignment.

> I meant case where user can copy-paste or cut-paste. It is so
> common that everybody know the accelerators
> (ctrl-x,ctrl-c,ctrl-v) and mouse gestures (drag-drop,
> ctrl-drag-drop) to those. I often use far more sophisticated
> features (like serializing-unserialising) at that spot but on
> lot of cases it is in essence copy or move exposed in GUI.

I'm not sure I follow you. Most applications aren't connected
to a terminal or a GUI window. And things like copy/paste are
purely GUI---how they map to application data depends on the
application. You could use assignment, but you're certainly not
obliged to. (For small scall copy/paste, classes like
`std::string` definitely should use assignment. For
copy/pasting larger entities: GUI objects generally need to know
their parent, and are usually polymorphic, so value assignment
doesn't work anyway.)

--
James

Öö Tiib

unread,
Mar 24, 2013, 5:55:59 PM3/24/13
to
On Sunday, 24 March 2013 21:26:22 UTC+2, James Kanze wrote:
> On Sunday, March 24, 2013 3:33:40 PM UTC, Öö Tiib wrote:
> > I meant case where user can copy-paste or cut-paste. It is so
> > common that everybody know the accelerators
> > (ctrl-x,ctrl-c,ctrl-v) and mouse gestures (drag-drop,
> > ctrl-drag-drop) to those. I often use far more sophisticated
> > features (like serializing-unserialising) at that spot but on
> > lot of cases it is in essence copy or move exposed in GUI.
>
> I'm not sure I follow you. Most applications aren't connected
> to a terminal or a GUI window.

OK, on such cases there are other "active parts" that may have similar
operations.

> And things like copy/paste are
> purely GUI---how they map to application data depends on the
> application.

I prefer relatively thin and specialized UI on top of possibly complex
application logic. UI designers are more oriented to taste and
usability and less oriented to deep logic and efficient algorithms.

> You could use assignment, but you're certainly not
> obliged to. (For small scall copy/paste, classes like
> `std::string` definitely should use assignment. For
> copy/pasting larger entities: GUI objects generally need to know
> their parent, and are usually polymorphic, so value assignment
> doesn't work anyway.)

Classes containing polymorphic members do not have to be polymorphic.
Value assignment may be used with those. Usually it is simple to
make efficient assignment of such objects with strong exception
guarantee. My point was that it *has* to be hand-made (as rule) to
achieve strong XG.

0 new messages