www.digitalmars.com         C & C++   DMDScript  

D - [opAssign] why no overload/forbidding for '='

reply Manfred Nowak <svv1999 hotmail.com> writes:
I am playing with a numerical class `number'.

First all ops and assigns were functions.

Then I switched to overloading `+' and `*' to use
  number a, b, c; // `new number' omitted
  a= b + c;
and changed my `unittest' cases accordingly. The trivial cases passed.
Then some errors occurred and I decided to include a `trace' field in the
class to allow tracing of the actions in a particular instance of `number'
by stating for example:
  b.trace= true;
  a= b + c;
This worked fine. However, when wanting to trace `a' by
  a.trace= true;
  a= b + c;
I trapped into the mess: the `trace' field of `a' was overwritten by the
`trace' field of `b + c' which of course was not set to `true'.

So I wanted to overload `=' to not overwrite the trace field. No way
because of the lack of `opAssign'. I had to include a property `set' to
`number' and change all existing `=' to `.set='. That was not only a
simple lexical change, because `=' was of course not only used for
assigning to `number'.

My number of test cases grew and I made intensive use of the trace
feature.

When getting a mysterious result I inserted a printout into a `while' loop
to observe a local variable. But there was no output. Conclusion: the loop
was not entered. Action: more outputs inserted into the enclosing scopes.
Observation: no output. ...

Final observation: did not use `.set=' but only `='. ARRGGGHHH!

However, forbidding of the use of pure `=', for example by declaring it
private, is also not possible.

Any suggestions?

So long. 

 

 
Feb 10 2004
next sibling parent Roberto Mariottini <Roberto_member pathlink.com> writes:
You seem to have a point here.
And it seems that nobody has an idea.

Anyone?

Ciao

In article <c0b6v5$1sel$1 digitaldaemon.com>, Manfred Nowak says...
I am playing with a numerical class `number'.

First all ops and assigns were functions.

Then I switched to overloading `+' and `*' to use
  number a, b, c; // `new number' omitted
  a= b + c;
and changed my `unittest' cases accordingly. The trivial cases passed.
Then some errors occurred and I decided to include a `trace' field in the
class to allow tracing of the actions in a particular instance of `number'
by stating for example:
  b.trace= true;
  a= b + c;
This worked fine. However, when wanting to trace `a' by
  a.trace= true;
  a= b + c;
I trapped into the mess: the `trace' field of `a' was overwritten by the
`trace' field of `b + c' which of course was not set to `true'.

So I wanted to overload `=' to not overwrite the trace field. No way
because of the lack of `opAssign'. I had to include a property `set' to
`number' and change all existing `=' to `.set='. That was not only a
simple lexical change, because `=' was of course not only used for
assigning to `number'.

My number of test cases grew and I made intensive use of the trace
feature.

When getting a mysterious result I inserted a printout into a `while' loop
to observe a local variable. But there was no output. Conclusion: the loop
was not entered. Action: more outputs inserted into the enclosing scopes.
Observation: no output. ...

Final observation: did not use `.set=' but only `='. ARRGGGHHH!

However, forbidding of the use of pure `=', for example by declaring it
private, is also not possible.

Any suggestions?

So long. 

 

 

Feb 11 2004
prev sibling next sibling parent "Ben Hinkle" <bhinkle4 juno.com> writes:
I'm not clear on the purpose of the trace field. My first suggestion is to make
the trace of the result of a
binary operation be true if the trace of either input is true:

 result.trace = x.trace || y.trace;

Remember objects are assigned by reference, not by copying the contents. I'd be
surprised if overloading
assignment is the best solution for your problem.

-Ben


"Manfred Nowak" <svv1999 hotmail.com> wrote in message
news:c0b6v5$1sel$1 digitaldaemon.com...
| I am playing with a numerical class `number'.
|
| First all ops and assigns were functions.
|
| Then I switched to overloading `+' and `*' to use
|   number a, b, c; // `new number' omitted
|   a= b + c;
| and changed my `unittest' cases accordingly. The trivial cases passed.
| Then some errors occurred and I decided to include a `trace' field in the
| class to allow tracing of the actions in a particular instance of `number'
| by stating for example:
|   b.trace= true;
|   a= b + c;
| This worked fine. However, when wanting to trace `a' by
|   a.trace= true;
|   a= b + c;
| I trapped into the mess: the `trace' field of `a' was overwritten by the
| `trace' field of `b + c' which of course was not set to `true'.
|
| So I wanted to overload `=' to not overwrite the trace field. No way
| because of the lack of `opAssign'. I had to include a property `set' to
| `number' and change all existing `=' to `.set='. That was not only a
| simple lexical change, because `=' was of course not only used for
| assigning to `number'.
|
| My number of test cases grew and I made intensive use of the trace
| feature.
|
| When getting a mysterious result I inserted a printout into a `while' loop
| to observe a local variable. But there was no output. Conclusion: the loop
| was not entered. Action: more outputs inserted into the enclosing scopes.
| Observation: no output. ...
|
| Final observation: did not use `.set=' but only `='. ARRGGGHHH!
|
| However, forbidding of the use of pure `=', for example by declaring it
| private, is also not possible.
|
| Any suggestions?
|
| So long.
|
|
|
|
Feb 11 2004
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
While it was 2/10/04 6:12 PM throughout the UK, Manfred Nowak sprinkled
little black dots on a white screen, and they fell thus:
<snip>
 I decided to include a `trace' field in the
 class to allow tracing of the actions in a particular instance of `number'
 by stating for example:
   b.trace= true;
   a= b + c;

You already have a problem here. D is not C++. Object variables aren't the objects themselves, but references to them.
 This worked fine. However, when wanting to trace `a' by
   a.trace= true;
   a= b + c;
 I trapped into the mess: the `trace' field of `a' was overwritten by 
 the `trace' field of `b + c' which of course was not set to `true'.

No it wasn't. The object reference 'a' was overwritten with a reference to a whole new object.
 So I wanted to overload `=' to not overwrite the trace field. No way 
 because of the lack of `opAssign'. I had to include a property `set'
 to `number' and change all existing `=' to `.set='. That was not only
 a simple lexical change, because `=' was of course not only used for 
 assigning to `number'.

The = operator on objects has a specific meaning as far as D is concerned - to copy object references, not the objects (or parts thereof) themselves. I think the only reason C++ allows overloading of = is that variables _are_ objects, not just references to them. Combining this with C++'s lack of GC, assignments sometimes need special handling. OTOH, in D, with its objects by reference and GC, being able to overload = was deemed pointless for anything but code obfuscation. Maybe you need to rethink your debug coding strategy. Or maybe someone else has an idea.... Stewart. -- My e-mail is valid but not my primary mailbox, aside from being the victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.
Feb 11 2004
next sibling parent reply Sam McCall <tunah.d tunah.net> writes:
 The = operator on objects has a specific meaning as far as D is
 concerned - to copy object references, not the objects (or parts
 thereof) themselves.
 
 I think the only reason C++ allows overloading of = is that variables 
 _are_ objects, not just references to them.  Combining this with C++'s 
 lack of GC, assignments sometimes need special handling.
 
 OTOH, in D, with its objects by reference and GC, being able to overload 
 = was deemed pointless for anything but code obfuscation.
 
 Maybe you need to rethink your debug coding strategy.  Or maybe someone 
 else has an idea....

Maybe D needs a "copy" operator. For value types it would be a direct copy, for classes it would default to memberwise copy and be overridable. (Should it copy members using itself or using =?, ie shallow or deep copy? Not sure. Maybe _two_ operators ;-) I'd like <- if it wasn't such a parsing nightmare, #= or = maybe? Sam PS is this like what Heron does? I haven't had a chance to look at it yet.
Feb 11 2004
next sibling parent J Anderson <REMOVEanderson badmama.com.au> writes:
Sam McCall wrote:

 Maybe D needs a "copy" operator. For value types it would be a direct 
 copy, for classes it would default to memberwise copy and be 
 overridable. (Should it copy members using itself or using =?, ie 
 shallow or deep copy? Not sure. Maybe _two_ operators ;-)
 I'd like <- if it wasn't such a parsing nightmare, #= or  = maybe?
 Sam

 PS is this like what Heron does? I haven't had a chance to look at it 
 yet.

I think it would be nice of have a deep and shallow assignment, since we already have a deep and shallow comparison. Parhaps something along those lines. = //Shallow == //Deep -- -Anderson: http://badmama.com.au/~anderson/
Feb 11 2004
prev sibling next sibling parent Matthias Becker <Matthias_member pathlink.com> writes:
Maybe D needs a "copy" operator. For value types it would be a direct 
copy, for classes it would default to memberwise copy and be 
overridable. (Should it copy members using itself or using =?, ie 
shallow or deep copy? Not sure. Maybe _two_ operators ;-)
I'd like <- if it wasn't such a parsing nightmare, #= or  = maybe?
Sam

PS is this like what Heron does? I haven't had a chance to look at it yet.

Well, in Heron you two different operators, one to copy the object, one to copy the reference itself. Eiffel has maechanisms for flat and deep copying.
Feb 11 2004
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
While it was 2/11/04 12:31 PM throughout the UK, Sam McCall sprinkled 
little black dots on a white screen, and they fell thus:

<snip>
 Maybe D needs a "copy" operator. For value types it would be a direct 
 copy, for classes it would default to memberwise copy and be 
 overridable. (Should it copy members using itself or using =?, ie 
 shallow or deep copy? Not sure. Maybe _two_ operators ;-)
 I'd like <- if it wasn't such a parsing nightmare, #= or  = maybe?
 Sam

Even still, there are at least two ways that object copying could work: - filling in the members of the destination object with the copied data (similar to C++) - would probably add unnecessary complications to both the D implementor and the D programmer. - creating a copy as a new object (similar to Java clone, and D dup on things that support it) - wouldn't by itself solve the OP's problem without further complications. And if we did implement a deep copy operator, we'd also need to think about our multiple references and circular data structures.
 PS is this like what Heron does? I haven't had a chance to look at it yet.

I see, another language that Foldoc still hasn't heard of. Maybe I'll look at it when I get the time.... -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.
Feb 11 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
news:c0d3ti$22u6$1 digitaldaemon.com...
 The = operator on objects has a specific meaning as far as D is
 concerned - to copy object references, not the objects (or parts
 thereof) themselves.

 I think the only reason C++ allows overloading of = is that variables
 _are_ objects, not just references to them.  Combining this with C++'s
 lack of GC, assignments sometimes need special handling.

 OTOH, in D, with its objects by reference and GC, being able to overload
 = was deemed pointless for anything but code obfuscation.

Most of the assignment operator overloading in C++ seems to be needed to just keep track of who owns the memory. So by using reference types coupled with GC, most of this just gets replaced with copying the reference itself. For example, given an array of class objects, the array's contents can be moved, sorted, shifted, etc., all without any need for overloaded assignments. Ditto for function parameters and return values. The references themselves just get moved about. There just doesn't seem to be any need for copying the entire contents of one class object into another pre-existing class object. Sometimes, one does need to create a copy of a class object, and for that one can still write a copy constructor in D, but they just don't seem to be needed remotely as much as in C++. Structs, being value objects, do get copied about. A copy is defined in D to be a bit copy. I've never been comfortable with any object in C++ that does something other than a bit copy when copied. Most of this other behavior stems from that old problem of trying to manage memory. Absent that, there doesn't seem to be a compelling rationale for allowing anything other than a bit copy.
Jun 01 2004
next sibling parent reply Daniel Horn <hellcatv hotmail.com> writes:
What if you want your structs to really pass by value, or if you want to 
get consistent behavior with any template argument.

For my BigRational struct it passes by value if you use an int or a 
long, but it passes by reference if you use a Int class (or some Int 
struct with an array) in the template.  Likewise an assign is a deep 
copy with long, and a shallow copy with a class.

It would be nice to get consistent behavior no matter the template argument.

Walter wrote:
 "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
 news:c0d3ti$22u6$1 digitaldaemon.com...
 
The = operator on objects has a specific meaning as far as D is
concerned - to copy object references, not the objects (or parts
thereof) themselves.

I think the only reason C++ allows overloading of = is that variables
_are_ objects, not just references to them.  Combining this with C++'s
lack of GC, assignments sometimes need special handling.

OTOH, in D, with its objects by reference and GC, being able to overload
= was deemed pointless for anything but code obfuscation.

Most of the assignment operator overloading in C++ seems to be needed to just keep track of who owns the memory. So by using reference types coupled with GC, most of this just gets replaced with copying the reference itself. For example, given an array of class objects, the array's contents can be moved, sorted, shifted, etc., all without any need for overloaded assignments. Ditto for function parameters and return values. The references themselves just get moved about. There just doesn't seem to be any need for copying the entire contents of one class object into another pre-existing class object. Sometimes, one does need to create a copy of a class object, and for that one can still write a copy constructor in D, but they just don't seem to be needed remotely as much as in C++. Structs, being value objects, do get copied about. A copy is defined in D to be a bit copy. I've never been comfortable with any object in C++ that does something other than a bit copy when copied. Most of this other behavior stems from that old problem of trying to manage memory. Absent that, there doesn't seem to be a compelling rationale for allowing anything other than a bit copy.

Jun 01 2004
parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <c9ingg$24uc$1 digitaldaemon.com>, Daniel Horn says...
For my BigRational struct it passes by value if you use an int or a 
long, but it passes by reference if you use a Int class

That should be harmless in this particular case, since Ints are immutable, so copying a reference is safe in the sense that the original will never get clobbered. However, I agree with you completely on the general point. Sometimes you do need to copy by value.
It would be nice to get consistent behavior no matter the template argument.

I agree with you again. There should ideally be some consistent mechanism for copying by value. I don't care if it's opEquals, copy-constructor, dup method, my suggested := operator, or whatever, but there should be a way of copying by value that has the same syntax for *any* type. (And it should fail at compile-time if you try to copy-by-value a class for which dup() or copy constructor is not defined).
Jun 01 2004
prev sibling next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <c9in1e$2461$1 digitaldaemon.com>, Walter says...
Most of the assignment operator overloading in C++ seems to be needed to
just keep track of who owns the memory. So by using reference types coupled
with GC, most of this just gets replaced with copying the reference itself.
For example, given an array of class objects, the array's contents can be
moved, sorted, shifted, etc., all without any need for overloaded
assignments. Ditto for function parameters and return values. The references
themselves just get moved about. There just doesn't seem to be any need for
copying the entire contents of one class object into another pre-existing
class object.

I'm tempted to ask, then, why the operators +=, *=, ++, and so on, are at all overloadable. It is a general expectation of programmers that the following sequence of statements:
       a = b;
       ++a;

will not modify b. C++ achieves this by allowing the = in the first statement to be overloaded to copy by value. D doesn't, which is why I had to forbid such operators on Int. (A struct would not have sufficed because I needed to have a destructor). If, as you say, "There just doesn't seem to be any need for copying the entire contents of one class object into another pre-existing class object", then what need do we have of assigning operator overloads. It would make sense to ban them altogether, but continue to allow them for structs. ..and for structs, of course, opAssign() also makes sense. Arcane Jill
Jun 01 2004
next sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Arcane Jill wrote:

<snip>
 I'm tempted to ask, then, why the operators +=, *=, ++, and so on, are at all
 overloadable.

Only postfix ++ and -- are overloadable. Prefix ++ and -- do the sensible thing. Just looking at the docs, " Assignment operator expressions, such as: a op= b are semantically equivalent to: a = a op b except that operand a is only evaluated once." Makes sense and is in line with C. But does this actually apply just as well if a is a class object, and doesn't have its own op<op>Assign? I guess that overloading is useful if you want to modify the object in place.... Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.
Jun 02 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:c9iv40$2ftu$1 digitaldaemon.com...
 I'm tempted to ask, then, why the operators +=, *=, ++, and so on, are at

 overloadable.

 It is a general expectation of programmers that the following sequence of
 statements:

       a = b;
       ++a;

will not modify b. C++ achieves this by allowing the = in the first

 be overloaded to copy by value. D doesn't, which is why I had to forbid

 operators on Int.

I got tripped up myself on this when I learned Java. Reference semantics are a different mindset, there's just no way around that. But once one gets comfortable with that different mindset, it isn't a problem anymore.
Jun 02 2004
parent reply hellcatv hotmail.com writes:
But what about overriding opAssign for structs...structs may or may not have
arrays inside...and this will determine whether they copy or not.
case in point is my template class that accepts an Int or a long...
depending on template arg the struct will do a shallow or deep copy
I agree that classes should be consistent: but the same goes for structs.



In article <c9l5ci$2ko3$2 digitaldaemon.com>, Walter says...
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:c9iv40$2ftu$1 digitaldaemon.com...
 I'm tempted to ask, then, why the operators +=, *=, ++, and so on, are at

 overloadable.

 It is a general expectation of programmers that the following sequence of
 statements:

       a = b;
       ++a;

will not modify b. C++ achieves this by allowing the = in the first

 be overloaded to copy by value. D doesn't, which is why I had to forbid

 operators on Int.

I got tripped up myself on this when I learned Java. Reference semantics are a different mindset, there's just no way around that. But once one gets comfortable with that different mindset, it isn't a problem anymore.

Jun 02 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <c9l836$2oob$1 digitaldaemon.com>, hellcatv hotmail.com says...
But what about overriding opAssign for structs...structs may or may not have
arrays inside...and this will determine whether they copy or not.
case in point is my template class that accepts an Int or a long...
depending on template arg the struct will do a shallow or deep copy

Ah - well, I'm off topic for the thread title here, but just quickly to mention... In the latest version I've actually removed the copy constructor and dup from Int, so you can't deep copy it any more. I did this because you don't need to deep copy it, ever, and I realized (in the end, after some prompting) that the presence of those functions might mislead people into thinking copy-by-value for this class did anything useful. Arcane Jill
Jun 02 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:c9la0f$2rpa$1 digitaldaemon.com...
 In the latest version I've actually removed the copy constructor and dup

 Int, so you can't deep copy it any more. I did this because you don't need

 deep copy it, ever, and I realized (in the end, after some prompting) that

 presence of those functions might mislead people into thinking

 this class did anything useful.

I've been thinking about the issue of BigInteger being implemented as a class, but desiring copy-by-value semantics. If you adopt the paradigm of copy-on-write for the internal array, wouldn't that achieve cbv semantics?
Jun 04 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <c9p8hq$2ja9$1 digitaldaemon.com>, Walter says...
I've been thinking about the issue of BigInteger being implemented as a
class, but desiring copy-by-value semantics. If you adopt the paradigm of
copy-on-write for the internal array, wouldn't that achieve cbv semantics?

Yes, you're probably right. I've been thinking the same thing. But I wouldn't have agreed with you until a few days' ago. You see, until a few days' ago, I would have considered that it *HAD* to be a class, because it is _unbelievably_ important to me that I am able to wipe memory after use (though only in a version(Secure) build, obviously - no need to give that performance hit to those who don't need it). Until a few days ago, I believed that having a destructor was the way to achieve that. But things have changed. You've told me that a destructor need not run. (In which case, I still don't see the point of allowing them! I'd still like for you to expand on this) Now that I know that, the decision to use a class becomes less clear. It is apparent that in the future I will have to write my own secure memory handler. That's ok - I've stuff like that before. But given that such a rewrite is on the cards, there is no longer any reason to keep it as a class. So yes, I could fairly easily change it to a struct - and this would have the added benefit of allowing me to implement +=, -=, ++ and so forth. But, Walter - structs don't have constructors, remember? So, that would leave me with no choice but to overload static opCall() to achieve the same effect. And while this would have the (rather nice) benefit of causing the word "new" to disappear from people's source code in relevant places - I rather thought you didn't like this approach. I mean, we're ending up with SOME things getting constructed with the syntax A(), OTHER things getting constructed with new A(), and YET MORE things offering both options. This is surely going to be confusing to people. Perhaps you might consider adopting my suggestion of making the keyword "new" optional in all cases, merging the functions of this() and static opCall(), and allowing structs to have constructors? Jill
Jun 04 2004
next sibling parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:c9p9ut$2lbr$1 digitaldaemon.com...
 In article <c9p8hq$2ja9$1 digitaldaemon.com>, Walter says...
I've been thinking about the issue of BigInteger being implemented as a
class, but desiring copy-by-value semantics. If you adopt the paradigm of
copy-on-write for the internal array, wouldn't that achieve cbv semantics?

Yes, you're probably right. I've been thinking the same thing. But I wouldn't have agreed with you until a few days' ago. You see, until a few days' ago, I would have considered that it *HAD* to be a class, because it is _unbelievably_ important to me that I am able to wipe memory after use (though only in a version(Secure) build, obviously - no need

 give that performance hit to those who don't need it). Until a few days ago, I
 believed that having a destructor was the way to achieve that.

 But things have changed. You've told me that a destructor need not run. (In
 which case, I still don't see the point of allowing them! I'd still like for

 to expand on this)

If your class is auto, then it will. However, auto currently has quite a restrictive use. Although that's likely to be relaxed in the future, it still may not be as general purpose as you require.
Jun 04 2004
prev sibling next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:c9p9ut$2lbr$1 digitaldaemon.com...
 You see, until a few days' ago, I would have considered that it *HAD* to

 class, because it is _unbelievably_ important to me that I am able to wipe
 memory after use (though only in a version(Secure) build, obviously - no

 give that performance hit to those who don't need it). Until a few days

 believed that having a destructor was the way to achieve that.

 But things have changed. You've told me that a destructor need not run.

 which case, I still don't see the point of allowing them! I'd still like

 to expand on this)

Lazy destruction, which happens in gc, is pretty much useless for things that *must* be deleted. A more aggressive scheme is required, such as I outlined before. But lazy destructors still are very handy for debugging and logging purposes.
 But, Walter - structs don't have constructors, remember? So, that would

 with no choice but to overload static opCall() to achieve the same effect.

 while this would have the (rather nice) benefit of causing the word "new"

 disappear from people's source code in relevant places - I rather thought

 didn't like this approach. I mean, we're ending up with SOME things

 constructed with the syntax A(), OTHER things getting constructed with new

 and YET MORE things offering both options. This is surely going to be

 to people. Perhaps you might consider adopting my suggestion of making the
 keyword "new" optional in all cases, merging the functions of this() and

 opCall(), and allowing structs to have constructors?

The trouble with allowing structs to have constructors, is then there's the issue of overloadable assignment operators and the default copy constructor, both of which I avoid. Then if there are constructors, why not destructors, and the whole complicated C++ mess appears (think what happens when you pass a struct as a function parameter). If everyone would be satisfied with 1) no destructors and 2) bit copies for assignment and copy constructor and 3) no RAII for structs, I would be much more amenable to adding them in <g>.
Jun 04 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <c9qiqn$1i01$1 digitaldaemon.com>, Walter says...
The trouble with allowing structs to have constructors, is then there's the
issue of overloadable assignment operators and the default copy constructor,
both of which I avoid. Then if there are constructors, why not destructors,
and the whole complicated C++ mess appears (think what happens when you pass
a struct as a function parameter).

Forgive me - I don't understand the problem. The way I see it, a struct constructor would just be an initialization function. It would function pretty much exactly like static opCall() does now. Overloadable assignment operators? You're talking about the much-discussed opAssign(), right? Ok - I *DO* see the problem. (I get there in the end). This is about parameter passing, right? If you pass a struct by value to a function, anything more complex than bit-copying is a pain in the neck. So you don't want opAssign(). Is that the problem?.
If everyone would be satisfied with 1) no destructors and 2) bit copies for
assignment and copy constructor and 3) no RAII for structs, I would be much
more amenable to adding them in <g>.

I don't have a problem with that at all. Structs should not have destructors, and I can think of a lot of reasons to justify that, but I don't see a problem with CONstructors. Like I said, people are using static opCall() for that very purpose right now, just to overcome that limitation (with the result of a nicer syntax even). And of course, if you ban destructors, then you also implicitly ban RAII. Goes without saying. Well, the way I see it is, if you give us constructors for structs, nobody is going to complain. (They might want more, but that's irrelevant). But wait - there's still something I'm not getting. Even if you do provide constructors for structs, why should anyone bother with them? What's the advantage over static opCall()? I mean, consider the following two lines:
       A a = A(parameters);
       A a = new A(parameters);

There are undoubtedly people who are going to prefer the first syntex. (I'm one of them). Now I'm starting to wonder why I should bother with constructors even for classes, when I can get the nicer syntax for those, too, with static opCall() instead. What's worse, though, is that it is currently possible for people to implement both syntaxes in a class AND HAVE THEM DO COMPLETELY DIFFERENT THINGS. I think this is a dangerous idea, and that the two functions really should be merged into one, so that "new" then becomes optional at the whim of the caller, instead of (as now) optional at the whim of the class designer. I hope this helps. I'm not just nitpicking because I don't like "new". Jill
Jun 04 2004
next sibling parent reply Kevin Bealer <Kevin_member pathlink.com> writes:
In article <c9qkjv$1ko4$1 digitaldaemon.com>, Arcane Jill says...
..
What's worse, though, is that it is currently possible for people to implement
both syntaxes in a class AND HAVE THEM DO COMPLETELY DIFFERENT THINGS. I think
this is a dangerous idea, and that the two functions really should be merged
into one, so that "new" then becomes optional at the whim of the caller, instead
of (as now) optional at the whim of the class designer.

I hope this helps. I'm not just nitpicking because I don't like "new".

Jill

Using opCall for non-new applications is only confusing if you typically overload the opCall to mean "new". The normal use of opCall (in my opinion) is as a "functor", i.e. "operator()" in C++. Suppose I have a function: flip(int a) { .. } ..and code that calls it. I can make it into an object, which is then used as a function, but also holds state and may have other methods. This ability to tack state, ie member vars, onto a function by converting into a class and using it as a functor is useful to a lot of folks. The C++ STL for example is designed to allow functors in almost all places where a function would otherwise be used. Your opCall syntax conflicts with that idiom. If the conflict between syntaxes is dangerous, then maybe you should switch to the side of the road that (almost) everyone else is driving on? Kevin
Jun 04 2004
next sibling parent "Carlos Santander B." <carlos8294 msn.com> writes:
"Kevin Bealer" <Kevin_member pathlink.com> escribió en el mensaje
news:c9qm08$1mkv$1 digitaldaemon.com
| Using opCall for non-new applications is only confusing if you typically
| overload the opCall to mean "new".  The normal use of opCall (in my
opinion) is
| as a "functor", i.e. "operator()" in C++.  Suppose I have a function:
|
| flip(int a)
| {
| ..
| }
|
| ..and code that calls it.  I can make it into an object, which is then
used as
| a function, but also holds state and may have other methods.
|
| This ability to tack state, ie member vars, onto a function by converting
into a
| class and using it as a functor is useful to a lot of folks.  The C++ STL
for
| example is designed to allow functors in almost all places where a
function
| would otherwise be used.  Your opCall syntax conflicts with that idiom.
|
| If the conflict between syntaxes is dangerous, then maybe you should
switch to
| the side of the road that (almost) everyone else is driving on?
|
| Kevin

Not really. The following compiles just fine:

class A
{
    static void opCall () {}
    void opCall (int a) {}
}

Notice that they don't take the same arguments. If they do, then there's a
compiler error about both functions conflicting, but I think that's a bug.

-----------------------
Carlos Santander Bernal
Jun 04 2004
prev sibling next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <c9qm08$1mkv$1 digitaldaemon.com>, Kevin Bealer says...

Using opCall for non-new applications is only confusing if you typically
overload the opCall to mean "new".  The normal use of opCall (in my opinion) is
as a "functor", i.e. "operator()" in C++.

I am perfectly well aware of that. I have been using functors for a very long time. There are one or two in Int, come to think of it. But you're talking about /non-static/ opCall. Believe it or not, in the D world, there is a /static/ opCall. This is somewhat pointless, since a static functor is just - well - a function. And because it looks so much like a constructor, and because structs don't have constructors, that's what gets used, despite the fact that it wasn't designed for that purpose. Arcane Jill
Jun 04 2004
next sibling parent "Ivan Senji" <ivan.senji public.srce.hr> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:c9rq7c$a78$1 digitaldaemon.com...
 In article <c9qm08$1mkv$1 digitaldaemon.com>, Kevin Bealer says...

Using opCall for non-new applications is only confusing if you typically
overload the opCall to mean "new".  The normal use of opCall (in my


as a "functor", i.e. "operator()" in C++.

I am perfectly well aware of that. I have been using functors for a very

 time. There are one or two in Int, come to think of it.

 But you're talking about /non-static/ opCall.

 Believe it or not, in the D world, there is a /static/ opCall. This is

 pointless, since a static functor is just - well - a function. And because

 looks so much like a constructor, and because structs don't have

 that's what gets used, despite the fact that it wasn't designed for that
 purpose.

D also has other operators in static versions: so it is possible to do something like: class A { static int opAdd(char[] x){} } and then you us it: A + "some string"; Having static operators IMO is a good thing because it gives us a flexibility in writing code. Someone might find it useful to define static opCall to do something other that constructing, so why not. The problem would be solved by letting structs have constructors (also not asking for destructors) and then there would be no confusion: use new to construct, and static opCall (if you need it) for something else.
 Arcane Jill

Jun 05 2004
prev sibling parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <c9rq7c$a78$1 digitaldaemon.com>, Arcane Jill says...
In article <c9qm08$1mkv$1 digitaldaemon.com>, Kevin Bealer says...

Using opCall for non-new applications is only confusing if you typically
overload the opCall to mean "new".  The normal use of opCall (in my opinion) is
as a "functor", i.e. "operator()" in C++.

I am perfectly well aware of that. I have been using functors for a very long time. There are one or two in Int, come to think of it. But you're talking about /non-static/ opCall. Believe it or not, in the D world, there is a /static/ opCall. This is somewhat pointless, since a static functor is just - well - a function. And because it looks so much like a constructor, and because structs don't have constructors, that's what gets used, despite the fact that it wasn't designed for that purpose. Arcane Jill

You're right.. sorry about that. Kevin
Jun 05 2004
prev sibling parent Matthias Becker <Matthias_member pathlink.com> writes:
Using opCall for non-new applications is only confusing if you typically
overload the opCall to mean "new".  The normal use of opCall (in my opinion) is
as a "functor", i.e. "operator()" in C++.  Suppose I have a function:

flip(int a)
{
..
}

..and code that calls it.  I can make it into an object, which is then used as
a function, but also holds state and may have other methods.

This ability to tack state, ie member vars, onto a function by converting into a
class and using it as a functor is useful to a lot of folks.  The C++ STL for
example is designed to allow functors in almost all places where a function
would otherwise be used.  Your opCall syntax conflicts with that idiom.

So your problem is that we break with a C++ idiom in D? Even if this were true, it would be stupid. But you forgot the small keyword "static". So the C++ idiom doesn't conflict with our "idiom" that you can find in a lot more languages than just one. Remember: We want: "Foo()" instead of "new Foo()". In C++ you already have this if you constuct on the stack! -- Matthias Becker
Aug 09 2004
prev sibling parent Matthias Becker <Matthias_member pathlink.com> writes:
 I'm not just nitpicking because I don't like "new".

Yes. new just doesn't make sense. It's more to type and it doesn't give any usefull information. In C++ you need new. .. = new Foo() // constructed on the heap .. = Foo() // constructed on the stack There are many languages, where you can construct things without new and everybody is happy with it. Why is "new" there in D? I never understood it. I didn't in Java, eigther. -- Matthias Becker
Aug 09 2004
prev sibling next sibling parent J Anderson <REMOVEanderson badmama.com.au> writes:
Walter wrote:

If everyone would be satisfied with 1) no destructors and 2) bit copies for
assignment and copy constructor and 3) no RAII for structs, I would be much
more amenable to adding them in <g>.
  

see structs as a light weight class so we don't want these other things in them anyway. I think this should be looked on from a practical stand-point (what will make this language better), rather then how to prevent arguments about the language (ie if this feature is left out then no-one can claim that D is flawed). Java left out pointers, so no-one can argue against all the evils of pointers but then no-one can make use of pointers in java either. -- -Anderson: http://badmama.com.au/~anderson/
Jun 05 2004
prev sibling next sibling parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <c9qiqn$1i01$1 digitaldaemon.com>, Walter says...

The trouble with allowing structs to have constructors, is <snip>

If everyone would be satisfied with 1) no destructors and 2) bit copies for
assignment and copy constructor and 3) no RAII for structs, I would be much
more amenable to adding them in <g>.

So, two months have gone by since the above quote, and pretty much everyone has said they could live with 1), 2) and 3). And now we have constructors for primitives. You see where this is leading...? The thing that we really need for generic programming, though, is consistency. As near as possible - the same syntax for all types. Consider the follwing possible D statements: (1) T x = new T(); (2) T* x = new T(); (3) T x = T(); (1) is appropriate for classes only; (2) is appropriate for primitives allocated on the heap only; (3) is appropriate for either structs or classes which happen to have overridden static opCall(). This is very counterintuitive. So, how hard would it be to make something like: # T x(); construct all types, as it does in C++? (Oh, and to have # int x(5); initialize x to 5, not 0, as it does in C++)? I'm not too bothered about the exact syntax (althernatives have been suggested by others), only that the syntax be the same for everything. Oh, and primitives and structs should still end up on the stack, not the heap - it's not /allocating/ them that's desirable, it's /initializing/ them (with consistent syntax). Arcane Jill
Aug 06 2004
prev sibling parent reply Matthias Becker <Matthias_member pathlink.com> writes:
If everyone would be satisfied with 1) no destructors and 2) bit copies for
assignment and copy constructor and 3) no RAII for structs, I would be much
more amenable to adding them in <g>.

I have no problim with not having destructors in structs. I'm not sure if I like 2). Why is it important? Imagine you have some caches for lazy evaluation or what ever. But hey, when we get C'tors for not having user defined assignment operatos (which we currently don't have too) I can live with that. And doesn't 1) imply 3)?
Aug 09 2004
parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <cf7liq$i01$1 digitaldaemon.com>, Matthias Becker says...

If everyone would be satisfied with 1) no destructors and 2) bit copies for
assignment and copy constructor and 3) no RAII for structs, I would be much
more amenable to adding them in <g>.


I have no problim with not having destructors in structs. 
I'm not sure if I like 2).

Which would you prefer? (a) no constructors for structs, at all, ever. (b) constructors for structs, but no bitwise copying. Remember - there is no (c). There is no third alternative - not according to Walter's quote above. You may think you're arguing for (c), but you're not. Walter's quote implies that only (a) and (b) are on offer. Nothing else. No other choice. If you reject (b), the only choice left is (a). And I suspect you didn't want inadvertantly to vote for that.
Why is it important?

Squillions of reasons: temporary results; returning values from functions; the list is endless. Also, recall that in C++, operator=() is effectively equivalent to a destructor call followed by a constructor call (and in some cases is actually implemented that way). But if we're ruling out destructors, what would opAssign() do that a constructor didn't?
And doesn't 1) imply 3)?

Yes. But (3) doesn't imply (1). Maybe Walter was expecting some folk to say "we want destructors but not RAII" or something. That didn't happen. Jill
Aug 09 2004
prev sibling parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
Arcane Jill wrote:

 You see, until a few days' ago, I would have considered that it *HAD* to
 be a class, because it is _unbelievably_ important to me that I am able to
 wipe memory after use

Why do you want to do that on a per-object basis? The probably cleaner, easier and more secure way might be to use a different allocator for the critical data: This allocator would then just place all sensitive data in an isolated block of memory. As soon as the critical portion of your code is over, you can simply wipe that block completely with an explicit command.
Jun 07 2004
parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <ca151q$1st0$1 digitaldaemon.com>, Norbert Nemec says...
Arcane Jill wrote:

 You see, until a few days' ago, I would have considered that it *HAD* to
 be a class, because it is _unbelievably_ important to me that I am able to
 wipe memory after use

Why do you want to do that on a per-object basis? The probably cleaner, easier and more secure way might be to use a different allocator for the critical data: This allocator would then just place all sensitive data in an isolated block of memory. As soon as the critical portion of your code is over, you can simply wipe that block completely with an explicit command.

Did that a few hours ago. Jill
Jun 07 2004
prev sibling next sibling parent hellcatv hotmail.com writes:
what if you have a struct holding an item it was templatized on

struct holder(T){
T value;
holder opAdd(...) {...}
}
then each time you assign to the struct you have to do some sort of templatized
copy of it that's specialized for each kind of T it can hold
perhaps it holds a capital Int, perhaps some other class that needs some
duplication... perhaps a long or an int or float.
suddenly you need a million different specializations to construct the class

template Constructor(T) {
T  makeSomething(T inp) {
return new T(inp);
}
}
template Constructor (T:mystruct) {
T makeSomething(T inp) { 
return T(inp);
}
}
template Constructor (T:float) {
float makeSoemthing (float inp) {
return inp;
}
...
and then the list goes on for all floats, ints, structs you might use...
each time a new primitive type is added you have to update your template
specializations.

is there any way we could templatize on construction mechanism
or on whether it's a class
or on whether it's a reference.

Another problem I'm facing is that my BigRational struct cannot have valid
values initially: it's faced with 2 null Integers, because a struct cannot be
assigned a class in its initial assignment phase.
I would enjoy having some sort of constructor run when the struct was created so
I could make  the numerator a new Int(0) and the denominator a new Int(1);
there's no no way to do this using the
struct Rational (T){
T numerator= ...;
T denom = ...;
..
};
it has been the source of a number of bugs... especially in the case that T
happens to be a long--because since it has to work for Int as well as Long, the
denominator gets assigned an (illegal) value of zero since I cannot assign the
denom to a default value of Constructor!(T).makeValue(1);  

any ideas for a good solution to this y'all? it would be nice if the language
offered me something like this. it seems as if structs are simply incomplete
without a way to set a reasonable default value that the compiler does not deign
to see as a "constant value"

In article <c9p8hq$2ja9$1 digitaldaemon.com>, Walter says...
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:c9la0f$2rpa$1 digitaldaemon.com...
 In the latest version I've actually removed the copy constructor and dup

 Int, so you can't deep copy it any more. I did this because you don't need

 deep copy it, ever, and I realized (in the end, after some prompting) that

 presence of those functions might mislead people into thinking

 this class did anything useful.

I've been thinking about the issue of BigInteger being implemented as a class, but desiring copy-by-value semantics. If you adopt the paradigm of copy-on-write for the internal array, wouldn't that achieve cbv semantics?

Jun 04 2004
prev sibling parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
Walter wrote:

 
 "Arcane Jill" <Arcane_member pathlink.com> wrote in message
 news:c9la0f$2rpa$1 digitaldaemon.com...
 In the latest version I've actually removed the copy constructor and dup

 Int, so you can't deep copy it any more. I did this because you don't
 need

 deep copy it, ever, and I realized (in the end, after some prompting)
 that

 presence of those functions might mislead people into thinking

 this class did anything useful.

I've been thinking about the issue of BigInteger being implemented as a class, but desiring copy-by-value semantics. If you adopt the paradigm of copy-on-write for the internal array, wouldn't that achieve cbv semantics?

If BigInteger is implemented as a class containing a reference to the data, this will not work: If you do a copy on write, this will change the reference inside the object, but other locations in the code might still hold a reference to the same object, so their value will be changed as well. What you would have to do, is copy-on-write for the whole object. For example: ------------------ class BigInteger { opAdd(BigInteger other) { BigInteger res = new BigInteger; res.value = ...; return res; } } ------------------ I don't even know, whether this should be called "copy-on-write" at all. It is more like considering BigInteger objects as read-only once they are constructed.
Jun 07 2004
parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <ca14k2$1rlj$1 digitaldaemon.com>, Norbert Nemec says...
If BigInteger is implemented as a class containing a reference to the data,
this will not work:

I'm smarter than that. When I said "implement copy by value semantics" you should implicitly read into my words "and make it work". I know how array references are stored, and how to play with them. Jill PS. The class is called Int. The phrase "big integer" is a suitable description, but it is not the name of the class, and should not be capitalized or concatenated.
Jun 07 2004
prev sibling next sibling parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
One other purpose of overloaded assignments in C++ not mentioned here are
implicit type conversions:

---------
        onetype A;
        anothertype B = A;
           // will call the constructor anothertype::anothertype(onetype)
        B = A;              
           // will call the assignment anothertype::operator=(onetype)
---------

I don't have this matter completely sorted out for D, but it seems
constructors for structs are a badly missing feature. Having both
constructors and overloaded assignments would probably not be necessary
(I've never seen anything useful but just duplicated code there in C++.)
But one of the two definitely would be needed.

Rationale: The expression template technique needs rather intelligent
assignements to work. Actually, all the action happens on
assignment/conversion. The whole expression is built up as one
tree-structure and only in the end it is decided how it should be
calculated. Without overloadable assignment/conversion, this will not be
possible.
Jun 01 2004
prev sibling parent reply Kevin Bealer <Kevin_member pathlink.com> writes:
In article <c9in1e$2461$1 digitaldaemon.com>, Walter says...
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
news:c0d3ti$22u6$1 digitaldaemon.com...
 The = operator on objects has a specific meaning as far as D is
 concerned - to copy object references, not the objects (or parts
 thereof) themselves.

 I think the only reason C++ allows overloading of = is that variables
 _are_ objects, not just references to them.  Combining this with C++'s
 lack of GC, assignments sometimes need special handling.

 OTOH, in D, with its objects by reference and GC, being able to overload
 = was deemed pointless for anything but code obfuscation.

Most of the assignment operator overloading in C++ seems to be needed to just keep track of who owns the memory. So by using reference types coupled with GC, most of this just gets replaced with copying the reference itself. For example, given an array of class objects, the array's contents can be moved, sorted, shifted, etc., all without any need for overloaded assignments. Ditto for function parameters and return values. The references themselves just get moved about. There just doesn't seem to be any need for copying the entire contents of one class object into another pre-existing class object. Sometimes, one does need to create a copy of a class object, and for that one can still write a copy constructor in D, but they just don't seem to be needed remotely as much as in C++. Structs, being value objects, do get copied about. A copy is defined in D to be a bit copy. I've never been comfortable with any object in C++ that does something other than a bit copy when copied. Most of this other behavior stems from that old problem of trying to manage memory. Absent that, there doesn't seem to be a compelling rationale for allowing anything other than a bit copy.

99% agreed, but objects that hold an open file, mmap, etc might want to at least handle the "I've been dupped" case, i.e. make a note not to close the file. Kevin
Jun 02 2004
next sibling parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
Kevin Bealer wrote:

 99% agreed, but objects that hold an open file, mmap, etc might want to at
 least handle the "I've been dupped" case, i.e. make a note not to close
 the file.

There are two ways to interpret this statement: 1. If you are talking about structs, then this approach would mean that you also need struct destructors. This again means lots of overhead and complications when copying around data (like for arguments, etc) 2. If you are talking about classes, this may either mean a) keeping track of references - again lots of overhead when copying around b) copying objects - in this case it is no problem keeping track of copies. Every construction and destruction of an object goes along with the call of constructors and destructors.
Jun 02 2004
parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <c9k15j$10kr$1 digitaldaemon.com>, Norbert Nemec says...
Kevin Bealer wrote:

 99% agreed, but objects that hold an open file, mmap, etc might want to at
 least handle the "I've been dupped" case, i.e. make a note not to close
 the file.

There are two ways to interpret this statement: 1. If you are talking about structs, then this approach would mean that you also need struct destructors. This again means lots of overhead and complications when copying around data (like for arguments, etc) 2. If you are talking about classes, this may either mean a) keeping track of references - again lots of overhead when copying around b) copying objects - in this case it is no problem keeping track of copies. Every construction and destruction of an object goes along with the call of constructors and destructors.

I would stipulate to classes. Okay... I was actually thinking of manually calling "close()", but I thought that the .dup was provided as a "byte level copy" for all object types, but I just checked and you have to define a property if you want that on your own classes. My bad. My hypothetical "file" class could define ".dup" to do a "dup2()" on the file descriptor if necessary. Kevin
Jun 02 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Kevin Bealer" <Kevin_member pathlink.com> wrote in message
news:c9juaj$rir$1 digitaldaemon.com...
Structs, being value objects, do get copied about. A copy is defined in D


be a bit copy. I've never been comfortable with any object in C++ that


something other than a bit copy when copied. Most of this other behavior
stems from that old problem of trying to manage memory. Absent that,


doesn't seem to be a compelling rationale for allowing anything other


bit copy.

99% agreed, but objects that hold an open file, mmap, etc might want to at

 handle the "I've been dupped" case, i.e. make a note not to close the

In such cases, an auto class should fill the bill instead of a struct.
Jun 02 2004
parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
Walter wrote:

 In such cases, an auto class should fill the bill instead of a struct.

Can references to auto classes be handled (copied, used as argument, returned, etc.) at all? I have not tried it, but from what I understand, an auto-object would have to be bound to exactly one reference variable, otherwise, it will not be clear when it should be deleted. Thinking about it, I realize, that I do not really understand auto variables at all: is it correct that only that object is deleted which is referenced the moment in which the variable goes out of scope? So, if I create an object of an auto class and then assign zero to the variable, the object will stay on the heap to be garbage-collected? So the "auto" keyword would not give you any security except for saving you the explicit "delete" at every exit point of the block? One more reason to abolish the concept of auto-classes! Auto-variables are a nice convenience, but if they don't add any security that the object really is deleted, then auto-classes are a pure nuisance, restricting the use of a class without adding value in terms of security.
Jun 02 2004
parent "Walter" <newshound digitalmars.com> writes:
"Norbert Nemec" <Norbert.Nemec gmx.de> wrote in message
news:c9ldq4$39$1 digitaldaemon.com...
 Walter wrote:

 In such cases, an auto class should fill the bill instead of a struct.

Can references to auto classes be handled (copied, used as argument, returned, etc.) at all? I have not tried it, but from what I understand,

 auto-object would have to be bound to exactly one reference variable,
 otherwise, it will not be clear when it should be deleted.

An auto reference can be passed as a function argument.
 Thinking about it, I realize, that I do not really understand auto

 at all: is it correct that only that object is deleted which is referenced
 the moment in which the variable goes out of scope?

Yes.
 So, if I create an
 object of an auto class and then assign zero to the variable, the object
 will stay on the heap to be garbage-collected? So the "auto" keyword would
 not give you any security except for saving you the explicit "delete" at
 every exit point of the block?

The compiler will insert a 'delete' on the reference at every exit point of the block, including exception unwinding. However, if you're clever and manage to have a copy of that reference 'leak' beyond the close of the local scope, it will be pointing to deleted data, and will cause a crash.
 One more reason to abolish the concept of auto-classes! Auto-variables are

 nice convenience, but if they don't add any security that the object

 is deleted, then auto-classes are a pure nuisance, restricting the use of

 class without adding value in terms of security.

They're just as secure as class objects on the stack in C++ are, in fact, even more secure.
Jun 04 2004