Topic: Interleaved execution of member initializers?


Author: johnchx2@yahoo.com
Date: Wed, 30 Mar 2005 23:07:59 CST
Raw View
Thomas Mang wrote:
> > Thomas Mang wrote:
> > > "Pete Becker" <petebecker@acm.org> schrieb im Newsbeitrag
> > > > the internals of operator new certainly modify
> > > > objects, in that they maintain internal state
> > > > that changes with allocations and
> > > > deallocations.
> > >
> > >
> > > Probably. Is it somewhere guaranteed? Is some magic forbidden?
.

If the allocation doesn't modify any state, why do you care when it
happens?  If it has no side effects, you won't miss them when they
don't happen.


> int* auto_ptrInternal(0);
>
> So I create a pointer out of nothing, so nothing was modified. Well,
> not out of nothing, out of some unused storage.

If I follow you, you are suggesting that initialization is not a side
effect, and is therefore unconstrained by sequence points.  I don't
think that any implementer would seriously believe this, but it
probably wouldn't do any harm to add "object initialization" to the
list in 1.9/7.

>From the implementer's perspective, initialization isn't any different
from assignment to a previously initialized object...it's all just
store instructions, which overwrite ("modify") whatever was stored at
that location in the first place.  (And yes, by the way, an object --
"a region of storage" -- is "bits.")

> Ok, so next question: Does memory leaking (or not leaking) add
> to observable behavior?

I'd put the question a different way: is a call to a deallocation
function observable behavior?  I think the answer here is "not
necessarily," but in general it's hard for the implementation to
guarantee that deallocation doesn't have observable effects, so it
generally can't optimize the call away.  (The problem is that a
deallocation function might call an OS function and the OS function
could make arbitrary changes in the application's execution environment
-- such as setting a global variable -- which could, in turn, affect
output.)

But more generally, I believe that a deallocation function that was a
no-op would be perfectly legal.  And in that case, assuming the
compiler could prove that deallocation was a no-op, it would be
permitted to optimize away the call, and to optimize away any side
effects which affect only that call.

Of course, it's also perfectly legal for an allocation function to do
nothing but throw std::bad_alloc every time you call it.  At some point
these things become "quality of implementation" issues.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: a9804814delthis@unet.univie.ac.at ("Thomas Mang")
Date: Thu, 31 Mar 2005 16:18:00 GMT
Raw View
<johnchx2@yahoo.com> schrieb im Newsbeitrag
news:1112210902.660288.36860@z14g2000cwz.googlegroups.com...
> Thomas Mang wrote:
> > > Thomas Mang wrote:
> > > > "Pete Becker" <petebecker@acm.org> schrieb im Newsbeitrag
> > > > > the internals of operator new certainly modify
> > > > > objects, in that they maintain internal state
> > > > > that changes with allocations and
> > > > > deallocations.
> > > >
> > > >
> > > > Probably. Is it somewhere guaranteed? Is some magic forbidden?
> .
>
> If the allocation doesn't modify any state, why do you care when it
> happens?  If it has no side effects, you won't miss them when they
> don't happen.


I am drawing a line between side-effects as described by the Standard, and
side-effects that happen on my system.


>
>
> > int* auto_ptrInternal(0);
> >
> > So I create a pointer out of nothing, so nothing was modified. Well,
> > not out of nothing, out of some unused storage.
>
> If I follow you, you are suggesting that initialization is not a side
> effect, and is therefore unconstrained by sequence points.  I don't
> think that any implementer would seriously believe this, but it
> probably wouldn't do any harm to add "object initialization" to the
> list in 1.9/7.


Agreed, although I am of the opinion it would not only do no harm, it would
help a lot.
But of course, it's the general problem of A saying (writing) something and
what B in turn understands (reads) - at some point, some further
interpretations are applied implicitly; sometimes into the good/correct
direction, but not necessarily.
The problem here is sometimes [practically not in this case here, though !]
people tend to read too much into the Standard, because of what is
intuitively expected.


>
> >From the implementer's perspective, initialization isn't any different
> from assignment to a previously initialized object...it's all just
> store instructions, which overwrite ("modify") whatever was stored at
> that location in the first place.  (And yes, by the way, an object --
> "a region of storage" -- is "bits.")


I don't think bits are objects in the C++ sense.
1.8/1: "An object has a type(3.9). The term object type refers to the type
with which the object is created".
The existence of bit-fields does not - in my opinion - support your
interpretation.

Also note that "modification" need not happen, if the bits already had the
correct value. Well, of course the machine instructions won't make any
difference, but for an outsider observing the bits, nothing changes. Part of
the confusion might arise because "modify" has no clear meaning here, and
IMHO the everyday-language meaning definitely leaves room for misconceptions
(if the technical aspect is considered).



> > Ok, so next question: Does memory leaking (or not leaking) add
> > to observable behavior?
>
> I'd put the question a different way: is a call to a deallocation
> function observable behavior?  I think the answer here is "not
> necessarily," but in general it's hard for the implementation to
> guarantee that deallocation doesn't have observable effects, so it
> generally can't optimize the call away.  (The problem is that a
> deallocation function might call an OS function and the OS function
> could make arbitrary changes in the application's execution environment
> -- such as setting a global variable -- which could, in turn, affect
> output.)
>
> But more generally, I believe that a deallocation function that was a
> no-op would be perfectly legal.  And in that case, assuming the
> compiler could prove that deallocation was a no-op, it would be
> permitted to optimize away the call, and to optimize away any side
> effects which affect only that call.
>
> Of course, it's also perfectly legal for an allocation function to do
> nothing but throw std::bad_alloc every time you call it.  At some point
> these things become "quality of implementation" issues.


Yes, I know, and I have explicitly mentioned this in my previous post. I
just wonder if here the freedom is really good, or if it's completely
redundant or even bad because something that everyone expects to happen is
not guaranteed.

Think about this expression (assuming built-in types):
i = h = j = 6;
IIRC, that's undefined behavior, still many people do it, and on no known
system it fails. And IIRC again, this will likely to be fixed in the next
version, making it defined behavior, because everyone counts on it
"working". Maybe making it undefined could improve performance or whatever
on whatever system, still it doesn't seem to matter.
[I know that comparison is not 100% perfect, but you should get the basic
idea].
By the same token, almost everyone assumes that allocating / releasing
memory is something that does "happen, and happens correctly". That doesn't
mean, of course, error conditions per se should be disallowed; they
certainly can't for allocating (I don't know if there are, for a correct
program that hasn't broken anything, reasonable error conditions for
deallocators) - and the current Standard leaves, as you have said correctly,
still room to do something completely stupid. OTOH, memory management (or
mismanagement) occasionally causes practically more "observable behavior"
than something else that formally IS obserable behavior. [note that some
time ago I have asked here what calls to library I/O functios are. The
feedback partially said "Who knows ?", and that other things like exiting
the program / returning a value to the OS should probably be observable
behavior too. So there might well be a general fix needed].


Thomas


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: johnchx2@yahoo.com
Date: Thu, 31 Mar 2005 21:47:36 CST
Raw View
"Thomas Mang" wrote:

> I am drawing a line between side-effects as described by the
> Standard, and side-effects that happen on my system.

I'm not sure I understand what you have in mind.  It sounds like you
think that an allocation function is allowed to return a pointer to
space that is not, in fact, allocated at the time the function returns.
 I don't see how that could be conforming.


> I don't think bits are objects in the C++ sense.

I meant that all objects "are" bits, not that all bits are objects.
But objects are objects before their lifetime begins, including before
initialization takes place.

> 1.8/1: "An object has a type(3.9). The term object type refers to
> the type with which the object is created".
> The existence of bit-fields does not - in my opinion - support your
> interpretation.

I don't see the relevance.  My point was that changing the bits stored
in the an object's value representation IS "modifying an object."


> Also note that "modification" need not happen, if the bits already
> had the correct value.

In which case we don't care when it does or doesn't happen.

> Part of
> the confusion might arise because "modify" has no clear meaning
> here, and IMHO the everyday-language meaning definitely leaves
> room for misconceptions (if the technical aspect is considered).

Maybe.  You seem to be saying that the value stored in an object's
value representation prior to intialization somehow "doesn't count"
when deciding whether a store instruction is a "modification."  Are you
seriously in doubt about whether changing the bits of an object's value
representation from whatever random values happened to the there to the
initializer value constitutes a "modification"?


> > Of course, it's also perfectly legal for an allocation function
> > to do nothing but throw std::bad_alloc every time you call it.
> > At some point these things become "quality of implementation"
> > issues.
>
>
> Yes, I know, and I have explicitly mentioned this in my previous
> post. I just wonder if here the freedom is really good, or if
> it's completely redundant or even bad because something that
> everyone expects to happen is not guaranteed.

What guarantee would you propose?  That the allocation function "try
really hard" to allocate memory?  How would such a guarantee be useful?



> Think about this expression (assuming built-in types):
> i = h = j = 6;
> IIRC, that's undefined behavior, still many people do it

Maybe I missed a DR, but this looks well-defined to me.  Are you sure
that's the expression you meant to type?


> By the same token, almost everyone assumes that allocating
> / releasing memory is something that does "happen, and
> happens correctly".

Again, I don't think I see any useful guarantees one might add.
Allocation is a request, which is always allowed to fail.  Deallocation
has no application-detectable effects at all. (For example, if you
deallocate X bytes, there can be no guarantee that you can subsequently
allocate X bytes, since the deallocation function is -- and should be
-- permitted to hand the space back to the OS, which may hand it to
some other process.)


> OTOH, memory management (or
> mismanagement) occasionally causes practically more "observable
> behavior" than something else that formally IS obserable behavior.

The problem is that one simply can't make useful guarantees about
resources like memory (beyond "tell the truth if allocation fails"), so
I don't see what would be gained.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: a9804814delthis@unet.univie.ac.at ("Thomas Mang")
Date: Sat, 2 Apr 2005 04:31:46 GMT
Raw View
<johnchx2@yahoo.com> schrieb im Newsbeitrag
news:1112292062.461269.243590@z14g2000cwz.googlegroups.com...
> "Thomas Mang" wrote:
>
> > I am drawing a line between side-effects as described by the
> > Standard, and side-effects that happen on my system.
>
> I'm not sure I understand what you have in mind.  It sounds like you
> think that an allocation function is allowed to return a pointer to
> space that is not, in fact, allocated at the time the function returns.
>  I don't see how that could be conforming.


No, I definitely mean something else. See below.


>
>
> > I don't think bits are objects in the C++ sense.
>
> I meant that all objects "are" bits, not that all bits are objects.
> But objects are objects before their lifetime begins, including before
> initialization takes place.
>
> > 1.8/1: "An object has a type(3.9). The term object type refers to
> > the type with which the object is created".
> > The existence of bit-fields does not - in my opinion - support your
> > interpretation.
>
> I don't see the relevance.  My point was that changing the bits stored
> in the an object's value representation IS "modifying an object."


Ah, I see, you refer somewhat to "an object is a region of storage", thus
implying modification of a region of storage (changing bit values) is
"modifying an object". I am linking modifying the object with it's
lifetime -> modified only if it has been constructed (constructor call
completed for non-trivial constructors); and that raw storage is not a (C++)
object.
Possibly 1.8 allows both interpretations



>
>
> > Also note that "modification" need not happen, if the bits already
> > had the correct value.
>
> In which case we don't care when it does or doesn't happen.
>
> > Part of
> > the confusion might arise because "modify" has no clear meaning
> > here, and IMHO the everyday-language meaning definitely leaves
> > room for misconceptions (if the technical aspect is considered).
>
> Maybe.  You seem to be saying that the value stored in an object's
> value representation prior to intialization somehow "doesn't count"
> when deciding whether a store instruction is a "modification."  Are you
> seriously in doubt about whether changing the bits of an object's value
> representation from whatever random values happened to the there to the
> initializer value constitutes a "modification"?


Please distinguish between being in doubt of what happens in the real-world,
and what the Standard says literally. It says "modifies an object". Objects
have lifetime. I read (literally) modifying an object to change it's state
(in whatever kind) after it has been constructed, after the constructor call
(if any) has completed.
It does not say "modifies a region of storage". However, I agree, since "an
object is a region of storage", such an interpretation might be possible. I
don't like it saying it that indirectly, and I am far from sure it says that
at all; I'd like an explicit wording much more. But maybe current wording is
sufficient. If there is consens it is, then nothing will change anyways, and
I can relax and will make a note to that para that I know - later - how to
read it correctly. But OTOH, there might also be consens clear wording
couldn't hurt.


>
>
> > > Of course, it's also perfectly legal for an allocation function
> > > to do nothing but throw std::bad_alloc every time you call it.
> > > At some point these things become "quality of implementation"
> > > issues.
> >
> >
> > Yes, I know, and I have explicitly mentioned this in my previous
> > post. I just wonder if here the freedom is really good, or if
> > it's completely redundant or even bad because something that
> > everyone expects to happen is not guaranteed.
>
> What guarantee would you propose?  That the allocation function "try
> really hard" to allocate memory?  How would such a guarantee be useful?


I am refering to the whole concept that an implementation can ignore
deallocation requests at will, because they add nothing to observable
behavior.

void * Mem = .....whereever
//... use Memory for observable behavior
::operator delete (Mem);

since the second line doesn't add to observable behavior (ignoring the
possibility of volatile objects in the allocation system), it is apparently
redundant. As you know, it is certainly not redundant in practice. And the
effect of having written that line, or not, add in _practice_ more to
observable behavior of the whole machine than e.g. writing to a local
memory-stream.
There is no need for error conditions at all.
Before responsing to that statement, please read on below.


>
>
>
> > Think about this expression (assuming built-in types):
> > i = h = j = 6;
> > IIRC, that's undefined behavior, still many people do it
>
> Maybe I missed a DR, but this looks well-defined to me.  Are you sure
> that's the expression you meant to type?


Frankly, partially yes, partially no.
I have done some research about this sequence-point read/write, rvalue
conversion stuff, volatile variables included.
My conclusion is: I am totally confused now.
I am fairly certain I have read some time ago, but not that long ago, above
expression can well yield undefined behavior, so it will be fixed. I see two
problems with that, however: First, I am not 100% the expression I am
referring at was semantically the presented expression. Second, the
reasoning it is currently undefined behavior might have been wrong, and I
simply missed the correction.

What's the state of the issue of above expression? Is it always defined, or
is it undefined? [with or without volatile objects?]


>
>
> > By the same token, almost everyone assumes that allocating
> > / releasing memory is something that does "happen, and
> > happens correctly".
>
> Again, I don't think I see any useful guarantees one might add.
> Allocation is a request, which is always allowed to fail.  Deallocation
> has no application-detectable effects at all. (For example, if you
> deallocate X bytes, there can be no guarantee that you can subsequently
> allocate X bytes, since the deallocation function is -- and should be
> -- permitted to hand the space back to the OS, which may hand it to
> some other process.)
>
>
> > OTOH, memory management (or
> > mismanagement) occasionally causes practically more "observable
> > behavior" than something else that formally IS obserable behavior.
>
> The problem is that one simply can't make useful guarantees about
> resources like memory (beyond "tell the truth if allocation fails"), so
> I don't see what would be gained.


Practically, nothing. No compiler optimizes away calls to deallocation
function. But unless I miss something, calls to deallocation functions seem
to be a no-op (observable behavior) regarding the current Standard; and some
no-ops are optimized away, precisely it was intended to let a compiler
optimize it away. Of course, since there are implementation limits and more
or less completely open ressource requirements (they have to be, of course,
open), any try to say deallocation shall happen can be overruled anyways.
But so can be almost everything.
But I agree, deallocating memory makes sense if it can also be later reused.
But nothing prevents (and it is impossible to enforce it, precisely - as you
have said - because of other processes) failing later allocation requests
(even if the implementation tries hard), even if all memory used priorily
was always returned properly, and less memory than used before was
requested.
So I can say that A (deallocating) does not really influence B (allocating
memory later), because B can always fail on its own. From that point of
view, enforcing A is completely useless. But if you consider the running
system as a whole, enforcing A can well make a difference. But of course,
this has little, if anything, to do with Standard C++.





Thomas


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Mon, 4 Apr 2005 12:56:55 GMT
Raw View
a9804814delthis@unet.univie.ac.at ("Thomas Mang") writes:

> Ah, I see, you refer somewhat to "an object is a region of storage",
> thus implying modification of a region of storage (changing bit
> values) is "modifying an object".

As we've seen several times recently ;-) it's possible to carry the
interpretation of that phrase too far.  For example, 12.8/15 says:

   "Whenever a temporary class object is copied using a copy
   constructor..."

If you view the object just as a region of storage, this seems to
imply some kind of memcpy operation.  We probably ought to change that
standard text from "is" to "has."

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net
Date: 4 Apr 2005 13:00:13 GMT
Raw View
"Thomas Mang" wrote:
.
> Ah, I see, you refer somewhat to "an object is a region of storage",
thus
> implying modification of a region of storage (changing bit values) is
> "modifying an object". I am linking modifying the object with it's
> lifetime -> modified only if it has been constructed (constructor
call
> completed for non-trivial constructors); and that raw storage is not
a (C++)
> object.
> Possibly 1.8 allows both interpretations

The standard doesn't define the word "modify", which means that
ordinary english usage applies. In ordinary english usage, a thing
doesn't have to be alive to be modified. That means that the issue is
an object's storage duration (3.7), not it's lifetime (3.8). All that's
required for an object to be modifiable is that it exist, not that it
be alive. In fact, bringing something to life is considered a
modification of the non-living substance that existed before it came to
life.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: johnchx2@yahoo.com
Date: Mon, 4 Apr 2005 20:34:59 CST
Raw View
"Thomas Mang" wrote:

> Ah, I see, you refer somewhat to "an object is a region of
> storage", thus implying modification of a region of storage
> (changing bit values) is "modifying an object". I am linking
> modifying the object with it's lifetime -> modified only if
> it has been constructed (constructor call completed for
> non-trivial constructors); and that raw storage is not a
> (C++) object.

3.8/1 says that the lifetime of an object of built-in type begins when
storage of the proper size and alignment is obtained.  In other words,
the lifetime of a built-in begins *before* initialization takes place.
So:

int i = 1;

creates an object of type int with indeterminate value, then modifies
that value by storing the value 1 to it.

The case of objects of class type with non-trivial constructors reduces
to the creation and subsequent initialization of sub-objects of
built-in type (eventually, if you recurse far enough), so the same
logic applies.

> But OTOH, there might also be consens clear wording
> couldn't hurt.

Agreed...I don't see any harm.


> Practically, nothing. No compiler optimizes away calls to
> deallocation function. But unless I miss something, calls
> to deallocation functions seem to be a no-op (observable
> behavior) regarding the current Standard; and some no-ops
> are optimized away, precisely it was intended to let a
> compiler optimize it away.

I don't think anything about the standard *guarantees* that a
deallocation function has no effect on observable behavior.  So, in
general, unless the compiler has specific knowledge of the particular
dealloation function in question, the call can't be optimized away.

For exampe, it would, I think, be legal to write a deallocation
function that writes some text to std::cout with every dealloacation.

As an example of a useful no-op dealloction function, consider a fully
garbage-collected C++ implementation.  Such an implementation might
want to ignore explicit deallocation calls entirely.  And if the
compiler were able to prove that the deallocation calls were no-ops, it
would be within its rights to optimize them away, no harm done.


> But if you consider the running system as a whole, enforcing
> A can well make a difference. But of course,
> this has little, if anything, to do with Standard C++.

I think that's true.  Put another way, there are many things that
programmers care about that aren't strictly issues of "standards
conformance."  Conformance isn't a sufficient (or, to be honest,
necessary) condition for a "useful" C++ implementation.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: a9804814delthis@unet.univie.ac.at ("Thomas Mang")
Date: Sat, 26 Mar 2005 06:43:39 GMT
Raw View
"Pete Becker" <petebecker@acm.org> schrieb im Newsbeitrag
news:SL-dnRIl4bV0rLvfRVn-3w@rcn.net...
> Thomas Mang wrote:
>
> > void f()
> > {
> > std::auto_ptr<int>(new int);
> > std::auto_ptr<double>(new double);
> > }
> >
> >
> > Can also leak if the 2 new expressions are evaluated before the
constructors
> > of the auto_ptrs.
> > This against all intuition and everything I have learnt about exception
> > safety, but where does it forbid this?
> >
>
> Sequence points!!!!! All side effects of the first statement must have
> been completed at the semicolon, and no side effects of the second
> statement may take place before that. Creating a new object calls a
> library function which modifies an object (the data structure that
> maintains the heap), so it is a side effect of the statement that
> contains it.


I am highly sorry for replying very late, I have completely forgotten that
thread.

When I posted, I have certainly checked the issue of sequence points. 1.9/7
is very useful (you are referring to that too), because that defines what
sequence points are, and here's my dilemma.
It says:

"Accessing an object designated by a volatile lvalue, modifying an object,
calling a library I/O function, or calling a function that does any of those
operations are all side effects". And then later what you have said above.

Where in the code snippet above have I invoked any of these? There is
clearly no volatile lvalue. There is not modifying an object (we are
creating one), and there is no library I/O function.
What forbids shuffling around the expresssions order and evaluating both new
expression first, before any auto_ptr - constructor is called?

BTW, general question: Is creating a memory leak undefined behavior? I ask,
because considering what is observable behavior and considering the as-if
rule, it can get even more complicated.


Thomas


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: petebecker@acm.org (Pete Becker)
Date: Mon, 28 Mar 2005 00:33:36 GMT
Raw View
Thomas Mang wrote:
>>>std::auto_ptr<int>(new int);
>>>std::auto_ptr<double>(new double);
>
> "Accessing an object designated by a volatile lvalue, modifying an object,
> calling a library I/O function, or calling a function that does any of those
> operations are all side effects". And then later what you have said above.
>
> Where in the code snippet above have I invoked any of these? There is
> clearly no volatile lvalue.

Really? Have you looked at the implementation of operator new? But more
to the point, the internals of operator new certainly modify objects, in
that they maintain internal state that changes with allocations and
deallocations.

> There is not modifying an object (we are
> creating one)

You're trying too hard.

> BTW, general question: Is creating a memory leak undefined behavior? I ask,
> because considering what is observable behavior and considering the as-if
> rule, it can get even more complicated.
>

If the behavior of your code is undefined it means simply that the C++
standard doesn't say what it does.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Thomas Mang" <a9804814delthis@unet.univie.ac.at>
Date: Mon, 28 Mar 2005 18:30:19 CST
Raw View
"Pete Becker" <petebecker@acm.org> schrieb im Newsbeitrag
news:jaCdnfxfj8cW6tjfRVn-2A@rcn.net...
> Thomas Mang wrote:
> >>>std::auto_ptr<int>(new int);
> >>>std::auto_ptr<double>(new double);
> >
> > "Accessing an object designated by a volatile lvalue, modifying an
object,
> > calling a library I/O function, or calling a function that does any of
those
> > operations are all side effects". And then later what you have said
above.
> >
> > Where in the code snippet above have I invoked any of these? There is
> > clearly no volatile lvalue.
>
> Really? Have you looked at the implementation of operator new? But more
> to the point, the internals of operator new certainly modify objects, in
> that they maintain internal state that changes with allocations and
> deallocations.


Probably. Is it somewhere guaranteed? Is some magic forbidden? Does solely
modifying objects at the operating system level (managed by the OS) count as
modifying objects too from a C++-point of view?



>
> > There is not modifying an object (we are
> > creating one)
>
> You're trying too hard.

[Not sure if the phrase "You're trying too hard" has special meaning to
native English speakers. I am assuming word-by-word-translation].

Well, I am reading what it says literally.
The term "modify" does in my opinion not include constructing objects in the
C++-sense. I have asked another person with no knowledge of C++, she was of
the same opinion - to modify means something has to exist already on which
to operate on.
Research on English Thesauruses did not reveal any information that
constructing something can be safely assumed to be synonymous for the term
modifying something.

Since it is pretty obvious - even to me - what was supposed to mean, you
might make an editorial change and make it say:
"constructing, modifying or destroying an object". How does that sound? And
if I have to try less hard then next time......


>
> > BTW, general question: Is creating a memory leak undefined behavior? I
ask,
> > because considering what is observable behavior and considering the
as-if
> > rule, it can get even more complicated.
> >
>
> If the behavior of your code is undefined it means simply that the C++
> standard doesn't say what it does.


The question was if that program is undefined behavior:

int main()
{
int * i = new int;
}


I couldn't find anything saying (directly or indirectly) that's undefined,
but I am far from being sure. That's why I ask.



Thomas


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net
Date: Mon, 28 Mar 2005 20:35:58 CST
Raw View
Thomas Mang wrote:
> "Pete Becker" <petebecker@acm.org> schrieb im Newsbeitrag
> news:jaCdnfxfj8cW6tjfRVn-2A@rcn.net...
.
> > Really? Have you looked at the implementation of operator new? But
more
> > to the point, the internals of operator new certainly modify
objects, in
> > that they maintain internal state that changes with allocations and
> > deallocations.
>
>
> Probably. Is it somewhere guaranteed? Is some magic forbidden? ...

Your fundamental concern was about the possibility of memory leakage:
memory being allocated, without being deallocated. Whatever it is that
keeps track of the fact that memory has been allocated involves at
least one thing that counts as an object, within the meaning of the
standard, even if the memory that it's stored in is not accessible by a
user program. That object has been modified by the change of state that
occurred to record the fact that the memory was allocated.

.

> ... Does solely
> modifying objects at the operating system level (managed by the OS)
count as
> modifying objects too from a C++-point of view?

Yes.

.
> Well, I am reading what it says literally.
> The term "modify" does in my opinion not include constructing objects
in the
> C++-sense. I have asked another person with no knowledge of C++, she
was of
> the same opinion - to modify means something has to exist already on
which
> to operate on.

A new expression has two main effects: the appropriate operator new()
is called, and the object is initialized. The only way an operator
new() can avoid modifying some object somewhere is if it doesn't
actually allocate any memory (i.e. placement new). There's a couple of
ways for initialization of an object to fail to modify it, but it's
pretty rare, and generally an example of bad design.

Still, if you use placement new and a do-nothing constructor, then I
would agree with you that no object has been modified. But what does
that have to do with this case? You're worrying about memory leaks, and
that can't be a issue as far as placement new is concerned.

> Research on English Thesauruses did not reveal any information that
> constructing something can be safely assumed to be synonymous for the
term
> modifying something.

Agreed, it's not a synonym. It is, however, still true that it's
generally impossible to construct things without modifying something.
You can't construct a house without modifying the materials that were
used to build it. You can't construct a sentence without modifying the
recording material it was recorded on, or the transmission medium that
it's being broadcast on. It is in precisely this sense that
non-placement construction of a C++ object is guaranteed to result in
the modification of some object, even if it's not the same object that
is being constructed.

> Since it is pretty obvious - even to me - what was supposed to mean,
you
> might make an editorial change and make it say:
> "constructing, modifying or destroying an object". How does that
sound? And
> if I have to try less hard then next time......

That would mean that placement new with a do-nothing constructor would
count as having a side effect; I don't see any reason why that should
be defined that way. The reason for calling something a side-effect is
that it can cause problems if it occurs in the wrong order. That can't
happen unless something is modified by the side effect.

.
> The question was if that program is undefined behavior:
>
> int main()
> {
> int * i = new int;
> }


The behavior of that program is perfectly well defined. The standard
says nothing that requires you to ever deallocate storage after having
allocated it.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Thomas Mang" <a9804814delthis@unet.univie.ac.at>
Date: Tue, 29 Mar 2005 01:54:57 CST
Raw View
<kuyper@wizard.net> schrieb im Newsbeitrag
news:1112061645.726298.85630@o13g2000cwo.googlegroups.com...
> Thomas Mang wrote:
> > "Pete Becker" <petebecker@acm.org> schrieb im Newsbeitrag
> > news:jaCdnfxfj8cW6tjfRVn-2A@rcn.net...
> .
> > > Really? Have you looked at the implementation of operator new? But
> more
> > > to the point, the internals of operator new certainly modify
> objects, in
> > > that they maintain internal state that changes with allocations and
> > > deallocations.
> >
> >
> > Probably. Is it somewhere guaranteed? Is some magic forbidden? ...
>
> Your fundamental concern was about the possibility of memory leakage:
> memory being allocated, without being deallocated. Whatever it is that
> keeps track of the fact that memory has been allocated involves at
> least one thing that counts as an object, within the meaning of the
> standard, even if the memory that it's stored in is not accessible by a
> user program. That object has been modified by the change of state that
> occurred to record the fact that the memory was allocated.


OK, here I agree with, at least partially. I cannot think of any way an
implementation can implement a working allocator without changing something
in whatever kind.
But note that, theoretically, the Standard does not guarantee somewhere that
allocating memory modifies something. So we are counting on what
implementations indeed do, not what the Standard says must happen [I
couldn't find anything].



> .
> > Well, I am reading what it says literally.
> > The term "modify" does in my opinion not include constructing objects
> in the
> > C++-sense. I have asked another person with no knowledge of C++, she
> was of
> > the same opinion - to modify means something has to exist already on
> which
> > to operate on.
>
> A new expression has two main effects: the appropriate operator new()
> is called, and the object is initialized. The only way an operator
> new() can avoid modifying some object somewhere is if it doesn't
> actually allocate any memory (i.e. placement new). There's a couple of
> ways for initialization of an object to fail to modify it, but it's
> pretty rare, and generally an example of bad design.
>
> Still, if you use placement new and a do-nothing constructor, then I
> would agree with you that no object has been modified. But what does
> that have to do with this case? You're worrying about memory leaks, and
> that can't be a issue as far as placement new is concerned.



Not sure if the problem is solved even assuming ::operator new modifies
something. For the moment, I assume it does. That makes each of the new
expressions a side effect, and since they are separated by a sequence point,
the first has to be evaluated before the second. Fine. What about the
auto_ptr - constructors? Need the first auto_ptr be constructed before the
second new expression is evaluated?



>
> > Research on English Thesauruses did not reveal any information that
> > constructing something can be safely assumed to be synonymous for the
> term
> > modifying something.
>
> Agreed, it's not a synonym. It is, however, still true that it's
> generally impossible to construct things without modifying something.


I don't think that holds in C++:

int* auto_ptrInternal(0);

So I create a pointer out of nothing, so nothing was modified. Well, not out
of nothing, out of some unused storage. Hmm, maybe that's modifying
something - I modify the bits. Bits are not objects, though. Maybe I modify
a reinterpret_cast<char&> of the underlying storage - since the lifetime of
a char starts as soon as the storage is obtained. Very far-fetched [but I
admit, maybe my whole concerns are also far-fetched], but what's your
opinion?


> You can't construct a house without modifying the materials that were
> used to build it. You can't construct a sentence without modifying the
> recording material it was recorded on, or the transmission medium that
> it's being broadcast on. It is in precisely this sense that
> non-placement construction of a C++ object is guaranteed to result in
> the modification of some object, even if it's not the same object that
> is being constructed.


Note at this point I can somewhat agree allocating memory modifies something
(though I still miss the explicit guarantee). Doesn't solve, in my eyes, the
auto_ptr - problem. If you can guide me to the right para, I am happy. The
interpretation I presented above is not really convincing to me.


[snip]

> > The question was if that program is undefined behavior:
> >
> > int main()
> > {
> > int * i = new int;
> > }
>
>
> The behavior of that program is perfectly well defined. The standard
> says nothing that requires you to ever deallocate storage after having
> allocated it.


Ok, so next question: Does memory leaking (or not leaking) add to observable
behavior? Maybe if the allocator has volatile values - don't know which
have, and which not. Are these two snippets equivalent regarding observable
behavior?

#1
{
int* i = new int(23);
}

#2
{
std::auto_ptr<int> p(new int);
}


If yes, what forbids (except stupidity, marketing reasons etc. -  which are
the real-world handicaps) optimizing away the auto_ptr completely (but still
evaluating the new-expression)?


Thanks for your time,

Thomas


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Usenet@aristeia.com (Scott Meyers)
Date: Tue, 1 Mar 2005 01:07:33 GMT
Raw View
Given

  class Widget { ... };

  void f(Widget *pw1, Widget *pw2);       // declare f

  f(new Widget, new Widget);              // call f

we know that we can leak memory in the call to f, because if the new
expression being used to initialize pw1 fully executes and then the new
expression being used to initialize pw2 throws, the results of the first
new expression won't be undone.  Fine, old news.

A colleague recently told me that the same applies to member initialization
lists:

  class Widget {
    Widget()
    : pw1(new Widget), pw2(new Widget)    // can leak if one new expression
    {}                                    // throws?

    Widget *pw1, *pw2;
  };

This would be true if evaluation of expressions in different member
initializers could be interleaved, but 12.6.2/3 says "There is a sequence
point (1.9) after the initialization of each base and member."  This makes
me think that such execution interleaving of member initializers is not
permitted.  Am I misreading something, or is my colleague mistaken?

Searching Google yielded a few threads on this topic, but none in this
newsgroup, and the certainty level of the posters in other threads was less
than reassuring...

Thanks,

Scott



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nevin@spies.com ("Nevin \":-]\" Liber")
Date: Tue, 1 Mar 2005 05:23:16 GMT
Raw View
In article <MPG.1c8d5c7acecee7a9897c9@news.hevanet.com>,
 Usenet@aristeia.com (Scott Meyers) wrote:

> A colleague recently told me that the same applies to member initialization
> lists:
>
>   class Widget {
>     Widget()
>     : pw1(new Widget), pw2(new Widget)    // can leak if one new expression
>     {}                                    // throws?
>
>     Widget *pw1, *pw2;
>   };
>
> This would be true if evaluation of expressions in different member
> initializers could be interleaved, but 12.6.2/3 says "There is a sequence
> point (1.9) after the initialization of each base and member."  This makes
> me think that such execution interleaving of member initializers is not
> permitted.  Am I misreading something, or is my colleague mistaken?

The code, as written, will leak if the second call to "new Widget"
throws.  But that really has very little to do with sequence points.

The types of pw1 and pw2 are "pointer to Widget", and are thus scalar
types.

In the first call to "new Widget", if either new throws or the Widget
constructor throws, the compiler cleans it up, and no leak.  Otherwise,

The first Widget object is now fully constructed.  Call this Object1.  A
pointer to Object1 is assigned to pw1, and pw1 is now (for lack of a
better term, since scalar types don't have constructors) fully
constructed.

Now we have a sequence point.

In the second call to "new Widget", if either new throws or the Widget
constructor throws, the compiler cleans up *that object*, and then
proceeds to clean up *the pointer* pw1.  However, since pw1 is a scalar
type, there is no destructor to call, and the object (formerly) pointed
to by pw1, aka Object1, is leaked.

Here is one potential fix:

  class Widget {
    Widget()
    : pw1(new Widget), pw2(new Widget)
    {}

    std::auto_ptr<Widget> pw1, pw2;
  };

Now, if the second call to "new Widget" throws, the compiler cleans up
that object, then calls the destructor for pw1, which cleans up Object1.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: petebecker@acm.org (Pete Becker)
Date: Tue, 1 Mar 2005 15:19:46 GMT
Raw View
nevin@spies.com wrote:
>
> Here is one potential fix:
>
>   class Widget {
>     Widget()
>     : pw1(new Widget), pw2(new Widget)
>     {}
>
>     std::auto_ptr<Widget> pw1, pw2;
>   };
>

I agree with your analysis of the sequence points and there
consequences. However, this fix introduces a whole new set of possible
problems: when such an object is copied, the orginal no longer holds any
Widgets. Be very careful about using auto_ptr's as data members.

A similar fix would be to use std::tr1::shared_ptr instead of auto_ptr.
That's still not the same as the original code, because all copies of a
Widget object share the same Widget*'s. And, of course, it requires an
implementation of TR1 (there aren't any yet) or, alternatively, the
Boost version of shard_ptr. (Okay, that's shared_ptr, but there's
something intriguing about that typo).

A third approach that works fine here (and creates objects with similar
semantics to the original, but without the possible object leaks) but
might be inefficient in other cases is:

Widget::Widget()
{
auto_ptr<Widget> p1(new Widget);
pw2 = new Widget;
pw1 = p1.release();
}

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nospam@nospam.ucar.edu ("Thomas Mang")
Date: Wed, 2 Mar 2005 20:11:44 GMT
Raw View
"Scott Meyers" <Usenet@aristeia.com> schrieb im Newsbeitrag
news:MPG.1c8d5c7acecee7a9897c9@news.hevanet.com...
> Given
>
>   class Widget { ... };
>
>   void f(Widget *pw1, Widget *pw2);       // declare f
>
>   f(new Widget, new Widget);              // call f
>
> we know that we can leak memory in the call to f, because if the new
> expression being used to initialize pw1 fully executes and then the new
> expression being used to initialize pw2 throws, the results of the first
> new expression won't be undone.  Fine, old news.
>
> A colleague recently told me that the same applies to member
initialization
> lists:
>
>   class Widget {
>     Widget()
>     : pw1(new Widget), pw2(new Widget)    // can leak if one new
expression
>     {}                                    // throws?
>
>     Widget *pw1, *pw2;
>   };
>
> This would be true if evaluation of expressions in different member
> initializers could be interleaved, but 12.6.2/3 says "There is a sequence
> point (1.9) after the initialization of each base and member."  This makes
> me think that such execution interleaving of member initializers is not
> permitted.  Am I misreading something, or is my colleague mistaken?


I am sure you know your example can leak anyways, simply if creating the
second Widget fails.

Trying to answer your question:

I am afraid your colleague is correct. The problem I see is 1.9/7 doesn't
cover creation of objects as side-effects. It says modifies an object,
that's not creating one. In the Standard Library introduction, it says
explicitly modifying function do not include constructors. Since expressions
separated by sequence points that do not participate in side effects can be
shuffled around safely also ignoring as-if (leaking is not observable
behavior here), I think a compiler evaluating both new expressions first and
then use the results to initialize the members is legal.
I don't even think there's a guarantee the second new expression will be
invoked after the first one.

Not sure though.


Thomas


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nospam@nospam.ucar.edu ("Thomas Mang")
Date: Wed, 2 Mar 2005 20:13:16 GMT
Raw View
Another note:

Assuming my interpretation in the other post.


Then this:


void f()
{
std::auto_ptr<int>(new int);
std::auto_ptr<double>(new double);
}


Can also leak if the 2 new expressions are evaluated before the constructors
of the auto_ptrs.
This against all intuition and everything I have learnt about exception
safety, but where does it forbid this?


Thomas


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Thu, 3 Mar 2005 06:25:01 GMT
Raw View
petebecker@acm.org (Pete Becker) writes:

> nevin@spies.com wrote:
>> Here is one potential fix:
>>   class Widget {
>>     Widget()
>>     : pw1(new Widget), pw2(new Widget)     {}
>>       std::auto_ptr<Widget> pw1, pw2;
>>   };
>>
>
> I agree with your analysis of the sequence points and there
> consequences. However, this fix introduces a whole new set of possible
> problems: when such an object is copied, the orginal no longer holds any
> Widgets. Be very careful about using auto_ptr's as data members.
>
> A similar fix would be to use std::tr1::shared_ptr instead of auto_ptr.
> That's still not the same as the original code, because all copies of a
> Widget object share the same Widget*'s. And, of course, it requires an
> implementation of TR1 (there aren't any yet) or, alternatively, the
> Boost version of shard_ptr. (Okay, that's shared_ptr, but there's
> something intriguing about that typo).

The boost::scoped_ptr template is the appropriate tool for this job.
It's non-copyable, so unless you give Widget explicit copy and and
assignment operators, it will be non-copyable and non-assignable too,
and won't inherit any strange behaviors.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: petebecker@acm.org (Pete Becker)
Date: Thu, 3 Mar 2005 06:48:08 GMT
Raw View
Thomas Mang wrote:

> void f()
> {
> std::auto_ptr<int>(new int);
> std::auto_ptr<double>(new double);
> }
>
>
> Can also leak if the 2 new expressions are evaluated before the constructors
> of the auto_ptrs.
> This against all intuition and everything I have learnt about exception
> safety, but where does it forbid this?
>

Sequence points!!!!! All side effects of the first statement must have
been completed at the semicolon, and no side effects of the second
statement may take place before that. Creating a new object calls a
library function which modifies an object (the data structure that
maintains the heap), so it is a side effect of the statement that
contains it.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: musiphil@bawi.org (Seungbeom Kim)
Date: Sat, 5 Mar 2005 00:11:57 GMT
Raw View
Pete Becker wrote:

> Sequence points!!!!! All side effects of the first statement must have
> been completed at the semicolon, and no side effects of the second
> statement may take place before that. Creating a new object calls a
> library function which modifies an object (the data structure that
> maintains the heap), so it is a side effect of the statement that
> contains it.

Is the data structure that mains the heap "an object" in the C++ sense,
as is mentioned in 1.9/7? I doubt it, though I'm not sure.

"Accessing an object designated by a volatile lvalue (3.10), modifying an
object, calling a library I/O function, or calling a function that does
any of those operations are all side effects, which are changes in the
state of the execution environment."

--
Seungbeom Kim

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: squell@alumina.nl (Marc Schoolderman)
Date: Sat, 5 Mar 2005 00:11:52 GMT
Raw View
Thomas Mang wrote:

> Assuming my interpretation in the other post.

May I summarize as "allocation in itself is not a side effect"?

> void f()
> {
> std::auto_ptr<int>(new int);
> std::auto_ptr<double>(new double);
> }

> Can also leak if the 2 new expressions are evaluated before the constructors
> of the auto_ptrs.
> This against all intuition and everything I have learnt about exception
> safety, but where does it forbid this?

You can go even further, if 'new' is side-effect free, a statement like:

     new int;

Might be optimized fully away, since any decent compiler will elide
temporaries which have no observable side effects.

1.9/7 describes side effects as 'changes in the execution environment'.
It probably doesn't list allocation explicitly because new and delete
are replaceable/overloadable, and can therefore in fact be side effect
free (I think this is true for placement new?).

Of course, we're concerned with the built-in allocation function. For
that one to be side-effect free, it must allocate memory while avoiding
any of the things 1.9/7 lists. That is, allocate memory by only
observing the execution environment. That's impossible.

In any case, I feel the best solution to the original problem would be
to simply use new (std::nothrow) instead of auto_ptr<> trickery.

~Marc

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: petebecker@acm.org (Pete Becker)
Date: Sat, 5 Mar 2005 00:12:20 GMT
Raw View
David Abrahams wrote:

>
> The boost::scoped_ptr template is the appropriate tool for this job.
> It's non-copyable, so unless you give Widget explicit copy and and
> assignment operators, it will be non-copyable and non-assignable too,
> and won't inherit any strange behaviors.
>

Actually, I just realized that we're both speculating about unstated
parts of this design. Since its only purpose was to illustrate a
particular issue, there's really no point in trying to correctly design
other parts of it. Besides, there's a much more significant problem that
neither one of us has tried to fix: each default-constructed Widget
object creates two new default-constructed Widgets objects.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]