[pointer container] copyability

I had this disagreement with Thorsten in Lillehammer (or was it Oxford?) but he was unimpressed with my arguments. I was wondering what Boost as a whole would think: My Thesis: the library should support copying and assignment of pointer containers using the usual copy/assignment syntax, rather than only allowing a copy via a clone() function. Rationale: Not being able to copy and assign pointer containers is an arbitrary restriction that limits usefulness and expressivity. The argument Thorsten gives against copyability is that the elements would have to be clone()d rather than copied, which is expensive because there's a dynamic allocation per element. I don't get it; we don't arbitrarily prohibit copying of std::vector<std::string> even though that incurs a dynamic allocation per element. Example: Consider std::pair<int,some_pointer_container>. Almost nothing works. IIUC, it's not copyable, can't be initialized, and can't be clone()d. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacnl8qsf.fsf@boost-consulting.com...
I had this disagreement with Thorsten in Lillehammer (or was it Oxford?) but he was unimpressed with my arguments. I was wondering what Boost as a whole would think:
My Thesis: the library should support copying and assignment of pointer containers using the usual copy/assignment syntax, rather than only allowing a copy via a clone() function.
In general I agree. Whatever costly it is in some cases where we don't bother about performance it could be convenient . Old principle - C++ programmer knows better - rules. Gennadiy

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacnl8qsf.fsf@boost-consulting.com... | | I had this disagreement with Thorsten in Lillehammer (or was it | Oxford?) but he was unimpressed with my arguments. I was wondering | what Boost as a whole would think: | | My Thesis: the library should support copying and assignment of | pointer containers using the usual copy/assignment syntax, rather | than only allowing a copy via a clone() function. | | Rationale: Not being able to copy and assign pointer containers is an | arbitrary restriction that limits usefulness and expressivity. The ^^^^^^^^ It was not an arbitrary choice. It makes little sense in my world to give the same syntax to different semantics. Oh, I cannot copy X, but if I put it into ptr_vector, I can. | argument Thorsten gives against copyability is that the elements | would have to be clone()d rather than copied, which is expensive | because there's a dynamic allocation per element. I don't get it; | we don't arbitrarily prohibit copying of std::vector<std::string> | even though that incurs a dynamic allocation per element. There is another point: if the compiler don't do RVO and NRVO and/or has to use assignment internally when handling vector< ptr_vector<T> >, it *really, really* hurts performance. I would like to see some benchmark that showed vector< ptr_vector<T> > would be as fast as ptr_vector< ptr_vector<T> >. Big objects are not nice to copy IMO; they should however be movable. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message news:d4lsa3$tq2$1@sea.gmane.org...
It was not an arbitrary choice. It makes little sense in my world to give the same syntax to different semantics. Oh, I cannot copy X, but if I put it into ptr_vector, I can. And that's great, if X has clone function
-- Pavel Chikulaev

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacnl8qsf.fsf@boost-consulting.com...
I had this disagreement with Thorsten in Lillehammer (or was it Oxford?) but he was unimpressed with my arguments.
With mine too :(
I was wondering what Boost as a whole would think:
My Thesis: the library should support copying and assignment of pointer containers using the usual copy/assignment syntax, rather than only allowing a copy via a clone() function.
Agreed. But we should provide some traits class to support Clone, CloneMe and so on. And some compile-time checking if user didn't provide any clone functions.
Rationale: Not being able to copy and assign pointer containers is an arbitrary restriction that limits usefulness and expressivity. The argument Thorsten gives against copyability is that the elements would have to be clone()d rather than copied, which is expensive because there's a dynamic allocation per element. I don't get it;
We might face with some problems with ptr_container's allocators and the only solution is something to it is something like: class Base { size_t size_of () const; // returns sizeof(*this); Base * placement_clone (void * address); }; Or support only heap_clone_allocator... which might be undesirable for some users. P.S. What's your opinion about comparison operators? User is most likely to going to have some polymorphic-related problems because C++ doesn't provide virtual function specifier "must be overriden in all derived classes". -- Pavel Chikulaev

"Pavel Chikulaev" <pavel.chikulaev@gmail.com> wrote in message news:d4ltl1$48d$1@sea.gmane.org...
Agreed. But we should provide some traits class to support Clone, CloneMe and so on. And some compile-time checking if user didn't provide any clone functions. And maybe one more traits class saying whether user wants ptr_containers of type T to be CopyConstructible and Assignable using cloning.
-- Pavel Chikulaev

On Apr 26, 2005, at 12:08 PM, David Abrahams wrote:
I had this disagreement with Thorsten in Lillehammer (or was it Oxford?) but he was unimpressed with my arguments. I was wondering what Boost as a whole would think:
My Thesis: the library should support copying and assignment of pointer containers using the usual copy/assignment syntax, rather than only allowing a copy via a clone() function.
Rationale: Not being able to copy and assign pointer containers is an arbitrary restriction that limits usefulness and expressivity. The argument Thorsten gives against copyability is that the elements would have to be clone()d rather than copied, which is expensive because there's a dynamic allocation per element. I don't get it; we don't arbitrarily prohibit copying of std::vector<std::string> even though that incurs a dynamic allocation per element.
Example: Consider std::pair<int,some_pointer_container>. Almost nothing works. IIUC, it's not copyable, can't be initialized, and can't be clone()d.
Why not just a clone_ptr<T> which calls clone on copy construction / assignment? Then you could std::container<clone_ptr<T>>. And such a container is copyable (clones on copy). An indirection iterator adaptor could ease use of this container with algorithms, as could indirect comparitors. std::list<clone_ptr<T>> might be the container of choice in the short term since list doesn't copy around its elements at the drop of a hat. In C++0X, (I believe) none of the containers will unnecessarily copy clone_ptr's, choosing to internally move them instead (moving a clone_ptr would be cheap). On your thesis: The library should only support copying and assignment of pointer containers using the usual copy/assignment syntax if a non-destructive copy of the pointer can be made. The proposed std::container<unique_ptr<T>> won't be such a container. Having std::container<auto_ptr<T>> fail at compile time is still a good idea. Otherwise it would destructively copy with copy syntax. <sigh> I realize these thoughts are a little ahead of their time at the moment due to the lack of compilers/libs which would make std::vector<clone_ptr<T>> acceptably efficient. -Howard

"Howard Hinnant" <hinnant@twcny.rr.com> wrote in message news:9ad77ce45f95bed2e20ddc3e0f66ed9d@twcny.rr.com... | On Apr 26, 2005, at 12:08 PM, David Abrahams wrote: | Why not just a clone_ptr<T> which calls clone on copy construction / | assignment? Then you could std::container<clone_ptr<T>>. And such a | container is copyable (clones on copy). An indirection iterator | adaptor could ease use of this container with algorithms, as could | indirect comparitors. See this is where we have different views: I dislike any confusion between copying and cloning and hiding one of them under the syntax of the other is just plain wrong IMO. They are conceptually different, just like copying and moving are different and just like value-based programming is different from OO-programming. | std::list<clone_ptr<T>> might be the container of choice in the short | term since list doesn't copy around its elements at the drop of a hat. | | In C++0X, (I believe) none of the containers will unnecessarily copy | clone_ptr's, choosing to internally move them instead (moving a | clone_ptr would be cheap). | | On your thesis: The library should only support copying and assignment | of pointer containers using the usual copy/assignment syntax if a | non-destructive copy of the pointer can be made. for a sufficiently weak definition of copying, then sometimes it will be possible, sometimes it won't. -Thorsten

Thorsten Ottosen wrote:
They [copying and cloning] are conceptually different, just like copying and moving are different and just like value-based programming is different from OO-programming.
Why are copying and cloning conceptually different and why is value-based programming different from OO programming?

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:090601c54aa7$0c9d5ac0$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > They [copying and cloning] are conceptually different, just like | > copying and moving are different and just like value-based | > programming is different from OO-programming. | | Why are copying and cloning conceptually different and why is value-based | programming different from OO programming? is this a joke? -Thorsten

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:090601c54aa7$0c9d5ac0$6401a8c0@pdimov2...
Thorsten Ottosen wrote:
They [copying and cloning] are conceptually different, just like copying and moving are different and just like value-based programming is different from OO-programming.
Why are copying and cloning conceptually different and why is value-based programming different from OO programming?
is this a joke?
No.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:094101c54aaa$f55dfb40$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:090601c54aa7$0c9d5ac0$6401a8c0@pdimov2... | >> Thorsten Ottosen wrote: | >>> They [copying and cloning] are conceptually different, just like | >>> copying and moving are different and just like value-based | >>> programming is different from OO-programming. | >> | >> Why are copying and cloning conceptually different and why is | >> value-based programming different from OO programming? | > | > is this a joke? | | No. I think you have to explain why they are not different then. -Thorsten

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:094101c54aaa$f55dfb40$6401a8c0@pdimov2...
Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:090601c54aa7$0c9d5ac0$6401a8c0@pdimov2...
Thorsten Ottosen wrote:
They [copying and cloning] are conceptually different, just like copying and moving are different and just like value-based programming is different from OO-programming.
Why are copying and cloning conceptually different and why is value-based programming different from OO programming?
is this a joke?
No.
I think you have to explain why they are not different then.
The burden of proof lies usually with the person making the statement. ;-) The reference semantics vs value semantics axis is orthogonal to OO, in my opinion. The fact that in C++ you can't (efficiently) have polymorphism with value semantics doesn't mean that copying a value and copying a polymorphic object are conceptually or fundamentally different. A cloning pointer is a very good approximation of a polymorphic value. I don't see why you consider it conceptually different from a value.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:099701c54ab2$8f8fb080$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > I think you have to explain why they are not different then. | | The burden of proof lies usually with the person making the statement. ;-) no, the burden of proof lies on the person making the controversial statement; in this case you. stuff like "value-based programming is not different from OO-programming" just seems weird to me; we probably won't get anywhere before we can agree on how to define these two terms. | The reference semantics vs value semantics axis is orthogonal to OO, in my | opinion. | The fact that in C++ you can't (efficiently) have polymorphism with | value semantics doesn't mean that copying a value and copying a polymorphic | object are conceptually or fundamentally different. | A cloning pointer is a very good approximation of a polymorphic value. I | don't see why you consider it conceptually different from a value. because you had to put "polymorphic" in front of "value" to describe what it means; polymorphic objects don't have value-based copy-semantics; you can't provide meaningful copy-constructor and copy-assignment operators. A "value object" implies something that behaves as an int; a "polymophic object" implies something that needs to allocated dynamically and which has virtual functions. trying to make a polymorphic object behave like a value object is confusing a best. -Thorsten

Thorsten Ottosen wrote:
because you had to put "polymorphic" in front of "value" to describe what it means; polymorphic objects don't have value-based copy-semantics; you can't provide meaningful copy-constructor and copy-assignment operators.
A cloning pointer holding a polymorphic object is CopyConstructible and Assignable. Why is this not meaningful? A vector of cloning pointers is also CopyConstructible and Assignable.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:001001c54ab9$32f6d6d0$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > because you had to put "polymorphic" in front of "value" to describe | > what it means; | > polymorphic objects don't have value-based copy-semantics; you can't | > provide meaningful copy-constructor | > and copy-assignment operators. | | A cloning pointer holding a polymorphic object is CopyConstructible and | Assignable. Why is this not meaningful? it could be, but know you're not talking about the original, wrapped polymorphic object. | A vector of cloning pointers is also CopyConstructible and Assignable. cool, then use that if you need to; but without move-semantics you're gonna pay performance wise. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
A "value object" implies something that behaves as an int; a "polymophic object" implies something that needs to allocated dynamically and which has virtual functions.
A polymorphic object doesn't need to be allocated dynamically.
trying to make a polymorphic object behave like a value object is confusing a best.
Who does it confuse? Pimpl is a commonly used and well understood pattern for doing just that. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uk6mo2ylf.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > A "value object" implies something that behaves as an int; a | > "polymophic object" implies something that needs to allocated | > dynamically and which has virtual functions. | | A polymorphic object doesn't need to be allocated dynamically. if you want it to act polymophic you have to. | > trying to make a polymorphic object behave like a value object is | > confusing a best. | | Who does it confuse? Pimpl is a commonly used and well understood | pattern for doing just that. Pimpl is an ugly hack for compilation firewalls. -Thorsten

Thorsten Ottosen <nesotto <at> cs.auc.dk> writes:
"David Abrahams" <dave <at> boost-consulting.com> wrote in message news:uk6mo2ylf.fsf <at> boost-consulting.com... | "Thorsten Ottosen" <nesotto <at> cs.auc.dk> writes: | | > A "value object" implies something that behaves as an int; a | > "polymophic object" implies something that needs to allocated | > dynamically and which has virtual functions. | | A polymorphic object doesn't need to be allocated dynamically.
if you want it to act polymophic you have to.
Nonsense: class B { /* abstract base class */ }; class D : public B { /* implementation */ }; void F(B& obj) { // call B virtual functions } void G() { D d; F(d); } How is D not being used polymorphically inside F()? Bob

"Bob Bell" <belvis@pacbell.net> wrote in message news:loom.20050427T182712-411@post.gmane.org... | Thorsten Ottosen <nesotto <at> cs.auc.dk> writes: | > "David Abrahams" <dave <at> boost-consulting.com> wrote in message | > news:uk6mo2ylf.fsf <at> boost-consulting.com... | > | "Thorsten Ottosen" <nesotto <at> cs.auc.dk> writes: | > | | > | > A "value object" implies something that behaves as an int; a | > | > "polymophic object" implies something that needs to allocated | > | > dynamically and which has virtual functions. | > | | > | A polymorphic object doesn't need to be allocated dynamically. | > | > if you want it to act polymophic you have to. | | Nonsense: let me refrase: you usually have to. -Thorsten

From: "Thorsten Ottosen" <nesotto@cs.auc.dk>
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uk6mo2ylf.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > A "value object" implies something that behaves as an int; a | > "polymophic object" implies something that needs to allocated | > dynamically and which has virtual functions. | | A polymorphic object doesn't need to be allocated dynamically.
if you want it to act polymophic you have to.
Pass it to a function taking a reference to a base class; it acts polymorphically. Where the object is allocated is orthogonal.
| > trying to make a polymorphic object behave like a value object is | > confusing a best. | | Who does it confuse? Pimpl is a commonly used and well understood | pattern for doing just that.
Pimpl is an ugly hack for compilation firewalls.
Regardless of your opinion of the technique, Dave's statement holds. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

"Rob Stewart" <stewart@sig.com> wrote in message news:200504271721.j3RHLiS27805@vanzandt.balstatdev.susq.com... | From: "Thorsten Ottosen" <nesotto@cs.auc.dk> | > | > trying to make a polymorphic object behave like a value object is | > | > confusing a best. | > | | > | Who does it confuse? Pimpl is a commonly used and well understood | > | pattern for doing just that. | > | > Pimpl is an ugly hack for compilation firewalls. | | Regardless of your opinion of the technique, Dave's statement | holds. there is no requirement that a Pimpl class should be copyable. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Rob Stewart" <stewart@sig.com> wrote in message news:200504271721.j3RHLiS27805@vanzandt.balstatdev.susq.com... | From: "Thorsten Ottosen" <nesotto@cs.auc.dk>
| > | > trying to make a polymorphic object behave like a value object is | > | > confusing a best. | > | | > | Who does it confuse? Pimpl is a commonly used and well understood | > | pattern for doing just that. | > | > Pimpl is an ugly hack for compilation firewalls. | | Regardless of your opinion of the technique, Dave's statement | holds.
there is no requirement that a Pimpl class should be copyable.
What is your point? When I wrote "Pimpl" I was referring to a programming idiom; people commonly use a clone() function on the impl to make a Pimpl copyable, and I claim it causes no major confusion. Whether or not some hypothetical textbook description of "Pimpl" has a copyability requirement is irrelevant. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uk6mo2ylf.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > A "value object" implies something that behaves as an int; a | > "polymophic object" implies something that needs to allocated | > dynamically and which has virtual functions. | | A polymorphic object doesn't need to be allocated dynamically.
if you want it to act polymophic you have to.
int f(Base& b) { return b.some_virtual_function(); // acts polymorphically } Derived d; // not dynamically allocated. int x = f(d);
| > trying to make a polymorphic object behave like a value object is | > confusing a best. | | Who does it confuse? Pimpl is a commonly used and well understood | pattern for doing just that.
Pimpl is an ugly hack for compilation firewalls.
It's a standard way to hide polymorphic behavior behind a statically-typed interface. Arguing that it is an ugly hack is just a distraction from the point at hand, so I'll ask again: Who is confused by Pimpl? -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:099701c54ab2$8f8fb080$6401a8c0@pdimov2... | Thorsten Ottosen wrote:
| > I think you have to explain why they are not different then. | The burden of proof lies usually with the person making the | statement. ;-)
no, the burden of proof lies on the person making the controversial statement; in this case you.
Controversialness is in the eye of the beholder, and regardless, Peter's right: by convention, the burden of proof lies with the claimant. That's very practical, because if you're saying something so obvious and "uncontroversial" it should be very _very_ simple to demonstrate it.
stuff like "value-based programming is not different from OO-programming"
Nobody made that statement.
just seems weird to me; we probably won't get anywhere before we can agree on how to define these two terms.
That's a good idea. Since you introduced the terms, it seems to me that it's on you to define them and explain why they're different.
| The reference semantics vs value semantics axis is orthogonal to OO, | in my opinion. The fact that in C++ you can't (efficiently) have | polymorphism with value semantics doesn't mean that copying a value | and copying a polymorphic object are conceptually or fundamentally | different. A cloning pointer is a very good approximation of a | polymorphic value. I don't see why you consider it conceptually | different from a value.
because you had to put "polymorphic" in front of "value" to describe what it means;
The user can't tell a Pimpl with a polymorphic impl from one without. They both just look like values, and all those behavioral variations that are implemented with virtual functions could just as well be implemented with "if" statements. Nobody (except maybe you) has a problem with allowing pimpls to have copy ctors -- impls often have clone() functions just to serve those copy ctors. A ptr_container is almost exactly like a multi-pimpl or a pimpl aggregate.
polymorphic objects don't have value-based copy-semantics; you can't provide meaningful copy-constructor and copy-assignment operators.
Sure they do. You only can't provide a meaningful copy ctor and copy assignment for an abstract base class.
A "value object" implies something that behaves as an int; a "polymophic object" implies something that needs to allocated dynamically and which has virtual functions.
trying to make a polymorphic object behave like a value object is confusing a best.
Pimpls don't confuse me. They "just work." -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:u8y342xnj.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:099701c54ab2$8f8fb080$6401a8c0@pdimov2... | > | Thorsten Ottosen wrote: | > | > | > I think you have to explain why they are not different then. | > | The burden of proof lies usually with the person making the | > | statement. ;-) | > | > no, the burden of proof lies on the person making the controversial | > statement; in this case you. | | Controversialness is in the eye of the beholder, and regardless, | Peter's right: by convention, the burden of proof lies with the | claimant. I disagree. | That's very practical, because if you're saying something | so obvious and "uncontroversial" it should be very _very_ simple to | demonstrate it. Pick up you're favourite book on OO and on STL and see if the programming techniques look alike. | > stuff like "value-based programming is not different from | > OO-programming" | | Nobody made that statement. Peter asked the question. | > | The reference semantics vs value semantics axis is orthogonal to OO, | > | in my opinion. The fact that in C++ you can't (efficiently) have | > | polymorphism with value semantics doesn't mean that copying a value | > | and copying a polymorphic object are conceptually or fundamentally | > | different. A cloning pointer is a very good approximation of a | > | polymorphic value. I don't see why you consider it conceptually | > | different from a value. | > | > because you had to put "polymorphic" in front of "value" to describe | > what it means; | | The user can't tell a Pimpl with a polymorphic impl from one without. | They both just look like values, and all those behavioral variations | that are implemented with virtual functions could just as well be | implemented with "if" statements. Nobody (except maybe you) has a | problem with allowing pimpls to have copy ctors -- impls often have | clone() functions just to serve those copy ctors. A ptr_container is | almost exactly like a multi-pimpl or a pimpl aggregate. I'm not prohibiting you from copying something with clone(), it just not the default behavior. | > polymorphic objects don't have value-based copy-semantics; you can't | > provide meaningful copy-constructor and copy-assignment operators. | | Sure they do. You only can't provide a meaningful copy ctor and copy | assignment for an abstract base class. and then you get the nice slicing behavior. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:u8y342xnj.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:099701c54ab2$8f8fb080$6401a8c0@pdimov2... | > | Thorsten Ottosen wrote: | > | > | > I think you have to explain why they are not different then. | > | The burden of proof lies usually with the person making the | > | statement. ;-) | > | > no, the burden of proof lies on the person making the controversial | > statement; in this case you. | | Controversialness is in the eye of the beholder, and regardless, | Peter's right: by convention, the burden of proof lies with the | claimant.
I disagree.
Then I will accept the burden of proof for my claim: http://en.wikipedia.org/wiki/Burden_of_proof#Other_uses http://www.nizkor.org/features/fallacies/burden-of-proof.html
| That's very practical, because if you're saying something | so obvious and "uncontroversial" it should be very _very_ simple to | demonstrate it.
Pick up you're favourite book on OO and on STL and see if the programming techniques look alike.
That's a very parsimonious explanation. It would be nice if you'd try to make it a bit clearer in the context of the question of duplicating objects, and show how it justifies a choice to prohibit ptr_containers from acting like proper values.
| > stuff like "value-based programming is not different from | > OO-programming" | | Nobody made that statement.
Peter asked the question.
That's very different from making the claim.
| The user can't tell a Pimpl with a polymorphic impl from one | without. They both just look like values, and all those | behavioral variations that are implemented with virtual functions | could just as well be implemented with "if" statements. Nobody | (except maybe you) has a problem with allowing pimpls to have copy | ctors -- impls often have clone() functions just to serve those | copy ctors. A ptr_container is almost exactly like a multi-pimpl | or a pimpl aggregate.
I'm not prohibiting you from copying something with clone(), it just not the default behavior.
Are you not prohibiting a ptr_container from being copied with copy syntax? Or have I misunderstood completely?
| > polymorphic objects don't have value-based copy-semantics; you can't | > provide meaningful copy-constructor and copy-assignment operators. | | Sure they do. You only can't provide a meaningful copy ctor and copy | assignment for an abstract base class.
and then you get the nice slicing behavior.
What slicing? struct Base { virtual ~Base(); virtual void f() = 0; protected: Base(Base const&) {} Base& operator=(Base const&) {} }; struct Derived : Base { virtual void f() {}; Derived(Derived const& x) : Base(x) {} Derived& operator=(Derived const& x) { Base& self = *this; self = x; } }; Furthermore, pimpls copy and assign quite nicely. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacnkw0cr.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > | Controversialness is in the eye of the beholder, and regardless, | > | Peter's right: by convention, the burden of proof lies with the | > | claimant. | > | > I disagree. | | Then I will accept the burden of proof for my claim: | http://en.wikipedia.org/wiki/Burden_of_proof#Other_uses | http://www.nizkor.org/features/fallacies/burden-of-proof.html I don't see how that changes anything. | > Pick up you're favourite book on OO and on STL and see if the | > programming techniques look alike. | | That's a very parsimonious explanation. It would be nice if you'd try | to make it a bit clearer in the context of the question of duplicating | objects, and show how it justifies a choice to prohibit ptr_containers | from acting like proper values. the first reason must be its damn expensive without move-semantics. the second reason is that value-based and OO programming are different and are best kept seperate; there are different idioms, different parts of the langauge is being used. copying is not that common an operation in OO programming; it is in value-based programming etc etc. there has to be one default (with good and bad sides); I've chosen the default that I feel, based on experience, fits the goal of the library best; that decision was to make sure the programmer only made copies when he really wanted to. you're analogy with vector<string> don't quite fit IMO. assigning vector<string> might be quite efficient with very few heap-allocations taking place; assigning ptr_vector<Foo> would cost the same as copy-construction + deallocation. | > | > stuff like "value-based programming is not different from | > | > OO-programming" | > | | > | Nobody made that statement. | > | > Peter asked the question. | | That's very different from making the claim. but it still feel a silly question to ask me to answer. -Thorsten

you're analogy with vector<string> don't quite fit IMO. assigning vector<string> might be quite efficient with very few heap-allocations taking place; assigning ptr_vector<Foo> would cost the same as copy-construction + deallocation.
IMO no performance point could justify omitting assignment operator. If I need vector<ptr_vector<Foo> > - it's my decision and I will deal with performance penalties. Gennadiy

"Gennadiy Rozental" <gennadiy.rozental@thomson.com> wrote in message news:d4ooik$f0h$1@sea.gmane.org...
you're analogy with vector<string> don't quite fit IMO. assigning vector<string> might be quite efficient with very few heap-allocations taking place; assigning ptr_vector<Foo> would cost the same as copy-construction + deallocation.
IMO no performance point could justify omitting assignment operator. If I need vector<ptr_vector<Foo> > - it's my decision and I will deal with performance penalties.
I agree very much with Gennadiy and others who argue that performance concerns should not cause an otherwise useful operation to be omitted. Some programs know in advance that they will never see large (in relation to processor speed or memory) numbers of elements in a particular container. The main considerations are convenience and functionality, not performance. "Premature optimization is the root of all evil." Hoare, Knuth --Beman

"Beman Dawes" <bdawes@acm.org> writes:
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> wrote in message news:d4ooik$f0h$1@sea.gmane.org...
you're analogy with vector<string> don't quite fit IMO. assigning vector<string> might be quite efficient with very few heap-allocations taking place; assigning ptr_vector<Foo> would cost the same as copy-construction + deallocation.
IMO no performance point could justify omitting assignment operator. If I need vector<ptr_vector<Foo> > - it's my decision and I will deal with performance penalties.
I agree very much with Gennadiy and others who argue that performance concerns should not cause an otherwise useful operation to be omitted.
In this case, the operation is not omitted at all, it's just spelled in a way that's incompatible with most of the rest of C++, in order to make certain things very inconvenient (i.e. "force you to think about the cost of copying"). That's what makes it so frustrating. Contrary to Peter, I have no serious problem with the idea that some achievable but poorly performing operations (e.g. operator + on a bidirectional iterator) might be omitted. I have a big problem with the idea that a function as basic as copy construction should be made available, but only with a nonstandard spelling. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacnkw0cr.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| > | Controversialness is in the eye of the beholder, and regardless, | > | Peter's right: by convention, the burden of proof lies with the | > | claimant. | > I disagree. | Then I will accept the burden of proof for my claim: | http://en.wikipedia.org/wiki/Burden_of_proof#Other_uses | http://www.nizkor.org/features/fallacies/burden-of-proof.html
I don't see how that changes anything.
Wow, you're tough. What sort of evidence would you accept as proof that by convention, the burden of proof lies with the claimant?
| > Pick up you're favourite book on OO and on STL and see if the | > programming techniques look alike. | | That's a very parsimonious explanation. It would be nice if you'd | try to make it a bit clearer in the context of the question of | duplicating objects, and show how it justifies a choice to prohibit | ptr_containers from acting like proper values.
the first reason must be its damn expensive without move-semantics.
I don't see how move semantics affects the cost of copying. When you have to copy, you have to copy. Anyway copying std::vector<std::string> is similarly expensive to copying ptr_container<whatever>. I don't see what's wrong with that.
the second reason is that value-based and OO programming are different and are best kept seperate; there are different idioms, different parts of the langauge is being used.
That's a very paternalistic approach to library design, and rather unprecedented. Normally the only reason to make functionality that you're implementing anyway hard to access is that it introduces potential correctness problems, e.g. undefined behavior. Efficiency considerations are usually only used when deciding whether or not to provide functionality at all. for example, that's why we don't have std::vector::push_front. Is there any amount of feedback from potential users that would impress you? So far a number of people in this thread have noted that the interface seems unduly restrictive. Have you heard from anyone who said "thank you, Thorsten, for preventing me from unintentionally doing something expensive?" Can you imagine it hapenning? For my part, this design detail and the associated response to feedback would prevent me from using the library altogether. I'd rather roll my own, should I need such a thing.
copying is not that common an operation in OO programming;
Maybe, but when you need it, you need it.
it is in value-based programming etc etc.
there has to be one default (with good and bad sides); I've chosen the default that I feel, based on experience, fits the goal of the library best; that decision was to make sure the programmer only made copies when he really wanted to.
As far as I can tell, it also prohibits him from making copies when he really wants to. There is no way to make a copy as part of an agreggate unless you control that agreggate's definition. There is no way to stick a ptr_container in another container... etc.
you're analogy with vector<string> don't quite fit IMO. assigning vector<string> might be quite efficient with very few heap-allocations taking place;
Or it might be very expensive.
assigning ptr_vector<Foo> would cost the same as copy-construction + deallocation.
It might only be necessary to do that for the elements and not the outer container. Anyway, I think I've made all the reasoned arguments that I can at this point. If nothing I've said makes an impression by now, I don't think there's much point in continuing to try. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On 4/27/05, David Abrahams <dave@boost-consulting.com> wrote:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacnkw0cr.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| > | Controversialness is in the eye of the beholder, and regardless, | > | Peter's right: by convention, the burden of proof lies with the | > | claimant. | > I disagree. | Then I will accept the burden of proof for my claim: | http://en.wikipedia.org/wiki/Burden_of_proof#Other_uses | http://www.nizkor.org/features/fallacies/burden-of-proof.html
I don't see how that changes anything.
Wow, you're tough. What sort of evidence would you accept as proof that by convention, the burden of proof lies with the claimant?
| > Pick up you're favourite book on OO and on STL and see if the | > programming techniques look alike. | | That's a very parsimonious explanation. It would be nice if you'd | try to make it a bit clearer in the context of the question of | duplicating objects, and show how it justifies a choice to prohibit | ptr_containers from acting like proper values.
the first reason must be its damn expensive without move-semantics.
I don't see how move semantics affects the cost of copying. When you have to copy, you have to copy.
Anyway copying std::vector<std::string> is similarly expensive to copying ptr_container<whatever>. I don't see what's wrong with that.
the second reason is that value-based and OO programming are different and are best kept seperate; there are different idioms, different parts of the langauge is being used.
That's a very paternalistic approach to library design, and rather unprecedented. Normally the only reason to make functionality that you're implementing anyway hard to access is that it introduces potential correctness problems, e.g. undefined behavior. Efficiency considerations are usually only used when deciding whether or not to provide functionality at all. for example, that's why we don't have std::vector::push_front.
Is there any amount of feedback from potential users that would impress you? So far a number of people in this thread have noted that the interface seems unduly restrictive. Have you heard from anyone who said "thank you, Thorsten, for preventing me from unintentionally doing something expensive?" Can you imagine it hapenning? For my part, this design detail and the associated response to feedback would prevent me from using the library altogether. I'd rather roll my own, should I need such a thing.
I needed to use a std::map<something, std::set<ExpensiveClass> >, at first I saw it as: std::map<something, boost::ptr_set<ExpensiveClass> >, and certainly it wasnt the way to do it... I just didnt do it because I saw it couldnt be done... correcting it to: boost::ptr_map<something, boost::ptr_set<ExpensiveClass> > So, well... I think it isnt that bad that it uses another syntax to make copies of ptr_containers... And I'm using ptr_containers all that time now...
copying is not that common an operation in OO programming;
Maybe, but when you need it, you need it.
it is in value-based programming etc etc.
there has to be one default (with good and bad sides); I've chosen the default that I feel, based on experience, fits the goal of the library best; that decision was to make sure the programmer only made copies when he really wanted to.
As far as I can tell, it also prohibits him from making copies when he really wants to. There is no way to make a copy as part of an agreggate unless you control that agreggate's definition. There is no way to stick a ptr_container in another container... etc.
you're analogy with vector<string> don't quite fit IMO. assigning vector<string> might be quite efficient with very few heap-allocations taking place;
Or it might be very expensive.
assigning ptr_vector<Foo> would cost the same as copy-construction + deallocation.
It might only be necessary to do that for the elements and not the outer container.
Anyway, I think I've made all the reasoned arguments that I can at this point. If nothing I've said makes an impression by now, I don't think there's much point in continuing to try.
-- Dave Abrahams Boost Consulting www.boost-consulting.com
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Felipe Magno de Almeida UIN: 2113442 email: felipe.almeida at ic unicamp br, felipe.m.almeida at gmail com, felipe at synergy com I am a C, modern C++, MFC, ODBC, Windows Services, MAPI developer from synergy, and Computer Science student from State University of Campinas(UNICAMP). To know more about: Unicamp: http://www.ic.unicamp.br Synergy: http://www.synergy.com.br current work: http://www.mintercept.com "There is no dark side of the moon really. Matter of fact it's all dark."

Felipe Magno de Almeida <felipe.m.almeida@gmail.com> writes:
Is there any amount of feedback from potential users that would impress you? So far a number of people in this thread have noted that the interface seems unduly restrictive. Have you heard from anyone who said "thank you, Thorsten, for preventing me from unintentionally doing something expensive?" Can you imagine it hapenning? For my part, this design detail and the associated response to feedback would prevent me from using the library altogether. I'd rather roll my own, should I need such a thing.
I needed to use a std::map<something, std::set<ExpensiveClass> >, at first I saw it as: std::map<something, boost::ptr_set<ExpensiveClass> >, and certainly it wasnt the way to do it... I just didnt do it because I saw it couldnt be done... correcting it to: boost::ptr_map<something, boost::ptr_set<ExpensiveClass> > So, well... I think it isnt that bad that it uses another syntax to make copies of ptr_containers... And I'm using ptr_containers all that time now...
The question isn't "were you able to work around it in this one case," but "was it helpful to have been prevented from writing it the natural way?" Note that the library doesn't provide workarounds when the outer thing isn't a standard container. Try std::vector<std::pair<int, boost::ptr_set<ExpensiveClass> > instead. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:015401c54b68$01274540$6401a8c0@pdimov2... | David Abrahams wrote: | > [...] Efficiency | > considerations are usually only used when deciding whether or not to | > provide functionality at all. for example, that's why we don't have | > std::vector::push_front. | | And this is very annoying when you really want to push_front. cont.insert( cont.begin(), new_element ); ? -Thorsten

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:015401c54b68$01274540$6401a8c0@pdimov2...
David Abrahams wrote:
[...] Efficiency considerations are usually only used when deciding whether or not to provide functionality at all. for example, that's why we don't have std::vector::push_front.
And this is very annoying when you really want to push_front.
cont.insert( cont.begin(), new_element );
?
Yes, that's what's annoying.

From: "Thorsten Ottosen" <nesotto@cs.auc.dk>
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:015401c54b68$01274540$6401a8c0@pdimov2... | David Abrahams wrote: | > [...] Efficiency | > considerations are usually only used when deciding whether or not to | > provide functionality at all. for example, that's why we don't have | > std::vector::push_front. | | And this is very annoying when you really want to push_front.
cont.insert( cont.begin(), new_element );
The different spelling complicates generic code. That's the same problem with pointer containers relying on clone(). -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uwtqot2ob.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "David Abrahams" <dave@boost-consulting.com> wrote in message | > news:uacnkw0cr.fsf@boost-consulting.com... | > | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > | > | > | Controversialness is in the eye of the beholder, and regardless, | > | > | Peter's right: by convention, the burden of proof lies with the | > | > | claimant. | > | > I disagree. | > | Then I will accept the burden of proof for my claim: | > | http://en.wikipedia.org/wiki/Burden_of_proof#Other_uses | > | http://www.nizkor.org/features/fallacies/burden-of-proof.html | > | > I don't see how that changes anything. | | Wow, you're tough. What sort of evidence would you accept as proof | that by convention, the burden of proof lies with the claimant? as some of your links says in the text, it is sometimes hard to agree who has the burden of proof. in this case I don't see that you are a claimant if you want to disallow copying anymore than if you would allow copying. | > | > Pick up you're favourite book on OO and on STL and see if the | > | > programming techniques look alike. | > | | > | That's a very parsimonious explanation. It would be nice if you'd | > | try to make it a bit clearer in the context of the question of | > | duplicating objects, and show how it justifies a choice to prohibit | > | ptr_containers from acting like proper values. | > | > the first reason must be its damn expensive without move-semantics. | | I don't see how move semantics affects the cost of copying. When you | have to copy, you have to copy. current containers will make temporary copies when moving objects around. | Anyway copying std::vector<std::string> is similarly expensive to | copying ptr_container<whatever>. I don't see what's wrong with that. | | > the second reason is that value-based and OO programming are different | > and are best kept seperate; there are different idioms, different | > parts of the langauge is being used. | | That's a very paternalistic approach to library design, and rather | unprecedented. Normally the only reason to make functionality that | you're implementing anyway hard to access is that it introduces | potential correctness problems, e.g. undefined behavior. Efficiency | considerations are usually only used when deciding whether or not to | provide functionality at all. for example, that's why we don't have | std::vector::push_front. | | Is there any amount of feedback from potential users that would | impress you? sure, but how many of these people participated in the review? | So far a number of people in this thread have noted that | the interface seems unduly restrictive. Have you heard from anyone | who said "thank you, Thorsten, for preventing me from unintentionally | doing something expensive?" Can you imagine it hapenning? not those exact words, but I've heard people say they agree on the design. I personally don't consider the "expensive copying" argument the most important. | For my | part, this design detail and the associated response to feedback would | prevent me from using the library altogether. I'd rather roll my own, | should I need such a thing. well, its quite easy to derive from one of the classes and add copy operations. | > copying is not that common an operation in OO programming; | | Maybe, but when you need it, you need it. so you just clone() the container. you might look at scoped_ptr<T> too. We could have made that do deep-copy too, but we didn't. Sometimes not copying by default is just better; C++ copies too much by default; that's why we're looking at "explicit class". | > it is in value-based programming etc etc. | > | > there has to be one default (with good and bad sides); I've chosen | > the default that I feel, based on experience, fits the goal of the | > library best; that decision was to make sure the programmer only | > made copies when he really wanted to. | | As far as I can tell, it also prohibits him from making copies when he | really wants to. it doesn't prohibit copying, it prohibits copies by use of copy-construction and copy-assignment, but yes, its a tradeoff. | There is no way to make a copy as part of an | agreggate unless you control that agreggate's definition. yes, you can't use make_pair() for example. | There is no | way to stick a ptr_container in another container... etc. correct, you have to use another pointer container. -Thorsten

David Abrahams writes:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
the second reason is that value-based and OO programming are different and are best kept seperate; there are different idioms, different parts of the langauge is being used.
That's a very paternalistic approach to library design, and rather unprecedented. Normally the only reason to make functionality that you're implementing anyway hard to access is that it introduces potential correctness problems, e.g. undefined behavior. Efficiency considerations are usually only used when deciding whether or not to provide functionality at all. for example, that's why we don't have std::vector::push_front.
To be fair, usually the "disabled" functionality is still available through other means, e.g. you can still do v.insert( v.begin(), ... ) // == v.push_front( ... ) and std::advance( bi, n ) // == bi + n So, in some sense, there is a number precedents supporting Thorsten's position. Not that it makes his particular design decision valid. -- Aleksey Gurtovoy MetaCommunications Engineering

Aleksey Gurtovoy <agurtovoy@meta-comm.com> writes:
David Abrahams writes:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
the second reason is that value-based and OO programming are different and are best kept seperate; there are different idioms, different parts of the langauge is being used.
That's a very paternalistic approach to library design, and rather unprecedented. Normally the only reason to make functionality that you're implementing anyway hard to access is that it introduces potential correctness problems, e.g. undefined behavior. Efficiency considerations are usually only used when deciding whether or not to provide functionality at all. for example, that's why we don't have std::vector::push_front.
To be fair, usually the "disabled" functionality is still available through other means, e.g. you can still do
v.insert( v.begin(), ... ) // == v.push_front( ... )
and
std::advance( bi, n ) // == bi + n
Yes, but the other part of the convention in those cases is that we only do it when there's a big-O difference. In the case of ptr_container clone() is O(N) just like a copy construction would be for any other container. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams writes:
Aleksey Gurtovoy <agurtovoy@meta-comm.com> writes:
David Abrahams writes:
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
the second reason is that value-based and OO programming are different and are best kept seperate; there are different idioms, different parts of the langauge is being used.
That's a very paternalistic approach to library design, and rather unprecedented. Normally the only reason to make functionality that you're implementing anyway hard to access is that it introduces potential correctness problems, e.g. undefined behavior. Efficiency considerations are usually only used when deciding whether or not to provide functionality at all. for example, that's why we don't have std::vector::push_front.
To be fair, usually the "disabled" functionality is still available through other means, e.g. you can still do
v.insert( v.begin(), ... ) // == v.push_front( ... )
and
std::advance( bi, n ) // == bi + n
Yes, but the other part of the convention in those cases is that we only do it when there's a big-O difference. In the case of ptr_container clone() is O(N) just like a copy construction would be for any other container.
Good point. -- Aleksey Gurtovoy MetaCommunications Engineering

Based on the recent discussions involving pointer container copyability, I thought I might want to get some opinions about a similar design decision made in singleton. Background: The singleton provides a smart pointer to access the instance, which does locking, null state detection, and possibly recreation under the covers when operator -> is used. As a result, it would be comparatively unsafe to use a raw pointer or a straight reference to the singleton instance, which can do none of these checks when being used to access a member of the singleton. Because of this, I did not provide an overloaded unary * operator for the singleton pointer class. However, there might be times when the singleton needs to be used polymorphically as some other base class or interface, and in such cases the only way to manage this is with a raw pointer or reference. Thus I currently provide a member function get_unsafe_ptr which actually returns a raw pointer to the instance. Question: Should I provide an operator * to return an 'unsafe reference', or add a function like get_unsafe_reference(), which forces the programmer to consider carefully before calling it? Should I add neither, and only leave get_unsafe_ptr? Are there cases in which the non-standard spelling of 'get a reference from the pointer' could cause problems? -Jason

From: Jason Hise <chaos@ezequal.com>
Based on the recent discussions involving pointer container copyability, I thought I might want to get some opinions about a similar design decision made in singleton.
Background:
The singleton provides a smart pointer to access the instance, which does locking, null state detection, and possibly recreation under the covers when operator -> is used. As a result, it would be comparatively unsafe to use a raw pointer or a straight reference to the singleton instance, which can do none of these checks when being used to access a member of the singleton. Because of this, I did not provide an overloaded unary * operator for the singleton pointer class. However, there might be times when the singleton needs to be used polymorphically as some other base class or interface, and in such cases the only way to manage this is with a raw pointer or reference. Thus I currently provide a member function get_unsafe_ptr which actually returns a raw pointer to the instance.
Question:
Should I provide an operator * to return an 'unsafe reference', or add a function like get_unsafe_reference(), which forces the programmer to consider carefully before calling it? Should I add neither, and only leave get_unsafe_ptr? Are there cases in which the non-standard spelling of 'get a reference from the pointer' could cause problems?
As I understand it, the operation is inherently unsafe; it isn't a matter of efficiency, or even desirable usage. Given that, it is most appropriate that you spell things differently. You want to call attention to such usage. Having said that, is it really the case that operator *() couldn't do the same work as operator ->() (possibly adding an exception if you can't get the underlying object)? Assuming you still don't provide operator *(), or you also want the raw semantics of your get_unsafe_*() function(s), I wonder if cast notation wouldn't be even better (versus providing access as a public member function): T * q(unsafe<T *>(p)); T & r(unsafe<T &>(p)); That is, unsafe() (whatever you might call it) would always take a singleton pointer, but depending upon whether it was parameterized to get a pointer or reference would do something different. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
As I understand it, the operation is inherently unsafe; it isn't a matter of efficiency, or even desirable usage. Given that, it is most appropriate that you spell things differently. You want to call attention to such usage.
Even though the usage is undesirable, it may still be necessary in cases where the singleton needs to behave polymorphically like one of its base classes.
Having said that, is it really the case that operator *() couldn't do the same work as operator ->() (possibly adding an exception if you can't get the underlying object)?
The difference is that the pointer returned by operator -> is guaranteed to be used immediately, and by using an intermediate pointer class I can construct a lock for the duration of the member call (this technique is described in Modern C++ Design, in the smart pointer section). Operator * is incapable of constructing this type of temporary lock, and additionally there is no guarantee that the reference returned would used and discarded immediately. Once the actual reference is returned, no more checking can take place.
Assuming you still don't provide operator *(), or you also want the raw semantics of your get_unsafe_*() function(s), I wonder if cast notation wouldn't be even better (versus providing access as a public member function):
T * q(unsafe<T *>(p)); T & r(unsafe<T &>(p));
That is, unsafe() (whatever you might call it) would always take a singleton pointer, but depending upon whether it was parameterized to get a pointer or reference would do something different.
I do like that syntax. Thanks for the suggestion, I'll see if I can make that work. -Jason

From: Jason Hise <chaos@ezequal.com>
Rob Stewart wrote:
As I understand it, the operation is inherently unsafe; it isn't a matter of efficiency, or even desirable usage. Given that, it is most appropriate that you spell things differently. You want to call attention to such usage.
Even though the usage is undesirable, it may still be necessary in cases where the singleton needs to behave polymorphically like one of its base classes.
But you said that the object may not exist since it is operator ->() that does the lifetime management. Thus, the object may not actually exist when you call the unsafe function, so it is a dangerous operation. Did I misunderstand something?
Having said that, is it really the case that operator *() couldn't do the same work as operator ->() (possibly adding an exception if you can't get the underlying object)?
The difference is that the pointer returned by operator -> is guaranteed to be used immediately, and by using an intermediate pointer class I can construct a lock for the duration of the member call (this technique is described in Modern C++ Design, in the smart pointer section).
So you're returning a proxy from operator ->(), right?
Operator * is incapable of constructing this type of temporary lock, and additionally there is no guarantee that the reference returned would used and discarded immediately. Once the actual reference is returned, no more checking can take place.
Why can't you return such a proxy from a member function like "get" or "get_object" and let that proxy convert to the singleton type? That means you can spell "get_unsafe_ptr" without "unsafe" because the proxy ensures that it is safe (as safe as with operator ->(), anyway). -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
From: Jason Hise <chaos@ezequal.com>
Rob Stewart wrote:
As I understand it, the operation is inherently unsafe; it isn't a matter of efficiency, or even desirable usage. Given that, it is most appropriate that you spell things differently. You want to call attention to such usage.
Even though the usage is undesirable, it may still be necessary in cases where the singleton needs to behave polymorphically like one of its base classes.
But you said that the object may not exist since it is operator ->() that does the lifetime management. Thus, the object may not actually exist when you call the unsafe function, so it is a dangerous operation. Did I misunderstand something?
operator-> is not solely responsible for lifetime management. Depending on the specific traits specified, automatic creation of the singleton instance can happen at various times. During a call to operator -> is just one of these times. There are explicit create and destroy member functions, and the singleton is generally scheduled to be automatically destroyed at one point or another. As a result, the automatic creation when operator -> is invoked is generally a last line of defense, which will recreate the instance if it has been destroyed (or in some rare circumstances will create the instance for the first time). So long as client code makes sure that the instance has already been created, that nothing destroys the instance while the raw pointer is in use, and that operations which need to be locked are locked manually, then that raw pointer is safe to use.
Having said that, is it really the case that operator *() couldn't do the same work as operator ->() (possibly adding an exception if you can't get the underlying object)?
The difference is that the pointer returned by operator -> is guaranteed to be used immediately, and by using an intermediate pointer class I can construct a lock for the duration of the member call (this technique is described in Modern C++ Design, in the smart pointer section).
So you're returning a proxy from operator ->(), right?
yes.
Operator * is incapable of constructing this type of temporary lock, and additionally there is no guarantee that the reference returned would used and discarded immediately. Once the actual reference is returned, no more checking can take place.
Why can't you return such a proxy from a member function like "get" or "get_object" and let that proxy convert to the singleton type?
That means you can spell "get_unsafe_ptr" without "unsafe" because the proxy ensures that it is safe (as safe as with operator ->(), anyway).
The action of getting the pointer itself is not unsafe. What is unsafe is maintaining that pointer and storing it for an extended period of time, during which the singleton could be destroyed and recreated. In almost every circumstance, it is preferable for client code to use and pass around singleton::pointers rather than singleton *s, just like it is preferable for client code to use smart pointers to manage memory rather than trying to manage memory and raw pointers manually. Managing them manually can be done safely, but it is harder and error prone. -Jason

From: Jason Hise <chaos@ezequal.com>
From: Jason Hise <chaos@ezequal.com>
Rob Stewart wrote:
There are explicit create and destroy member functions, and the singleton is generally scheduled to be automatically destroyed at one
Rob Stewart wrote: point or another. As a result, the automatic creation when operator -> is invoked is generally a last line of defense, which will recreate the instance if it has been destroyed (or in some rare circumstances will create the instance for the first time).
So long as client code makes sure that the instance has already been created, that nothing destroys the instance while the raw pointer is in use, and that operations which need to be locked are locked manually, then that raw pointer is safe to use.
Do the same problems exist when using the singleton object within the context of operator ->()?
Having said that, is it really the case that operator *() couldn't do the same work as operator ->() (possibly adding an exception if you can't get the underlying object)?
The difference is that the pointer returned by operator -> is guaranteed to be used immediately, and by using an intermediate pointer class I can construct a lock for the duration of the member call (this technique is described in Modern C++ Design, in the smart pointer section).
Note that I can write this: class A { ... }; // whatever the proxy's name is: typedef boost::singleton<A>::some_proxy proxy_type; proxy_type proxy(s.operator ->()); proxy->whatever();
Operator * is incapable of constructing this type of temporary lock, and additionally there is no guarantee that the reference returned would used and discarded immediately. Once the actual reference is returned, no more checking can take place.
Why can't you return such a proxy from a member function like "get" or "get_object" and let that proxy convert to the singleton type?
That means I can write A & object(*proxy); or A & object(proxy.get_object()); and then I can do what I like with object.
That means you can spell "get_unsafe_ptr" without "unsafe" because the proxy ensures that it is safe (as safe as with operator ->(), anyway).
The action of getting the pointer itself is not unsafe. What is unsafe is maintaining that pointer and storing it for an extended period of time, during which the singleton could be destroyed and recreated.
Right. As I see it, you've taken steps to ensure that the singleton survives so long as the proxy returned from operator ->() lives. Similarly, operator *() can return the same (or a similar) proxy thus keeping the object alive during its use. If that proxy provides a conversion operator (gasp!) to the object managed by the singleton smart pointer, then you get complete safety and access to the raw object. The conversion operator (probably only appropriate on a second proxy type) make this possible: A & object(proxy);
In almost every circumstance, it is preferable for client code to use and pass around singleton::pointers rather than singleton *s, just like it is preferable for client code to use smart pointers to manage memory rather than trying to manage memory and raw pointers manually. Managing them manually can be done safely, but it is harder and error prone.
I understand the motivation for the original question. I'm just having a hard time getting you to see my point, it would seem. Hopefully, spelling out more of the details has helped. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
From: Jason Hise <chaos@ezequal.com>
There are explicit create and destroy member functions, and the singleton is generally scheduled to be automatically destroyed at one point or another. As a result, the automatic creation when operator -> is invoked is generally a last line of defense, which will recreate the instance if it has been destroyed (or in some rare circumstances will create the instance for the first time).
So long as client code makes sure that the instance has already been created, that nothing destroys the instance while the raw pointer is in use, and that operations which need to be locked are locked manually, then that raw pointer is safe to use.
Do the same problems exist when using the singleton object within the context of operator ->()?
Only if the member function being called destroys the instance, which is the moral equivalent of "delete this;"
Having said that, is it really the case that operator *() couldn't do the same work as operator ->() (possibly adding an exception if you can't get the underlying object)?
The difference is that the pointer returned by operator -> is guaranteed to be used immediately, and by using an intermediate pointer class I can construct a lock for the duration of the member call (this technique is described in Modern C++ Design, in the smart pointer section).
Note that I can write this:
class A { ... }; // whatever the proxy's name is: typedef boost::singleton<A>::some_proxy proxy_type; proxy_type proxy(s.operator ->()); proxy->whatever();
Actually, the proxy type (which is called access_pointer in the code) is a private member class of the pointer class, and thus variables of that type cannot be created or talked about by client code.
Operator * is incapable of constructing this type of temporary lock, and additionally there is no guarantee that the reference returned would used and discarded immediately. Once the actual reference is returned, no more checking can take place.
Why can't you return such a proxy from a member function like "get" or "get_object" and let that proxy convert to the singleton type?
That means I can write
A & object(*proxy);
or
A & object(proxy.get_object());
and then I can do what I like with object.
Ahh, I think I understand now. So essentially, pointer::operator * () would return a 'smart reference' class instance?
That means you can spell "get_unsafe_ptr" without "unsafe" because the proxy ensures that it is safe (as safe as with operator ->(), anyway).
The action of getting the pointer itself is not unsafe. What is unsafe is maintaining that pointer and storing it for an extended period of time, during which the singleton could be destroyed and recreated.
Right. As I see it, you've taken steps to ensure that the singleton survives so long as the proxy returned from operator ->() lives.
Sort of. I ensure that the singleton is created immediately before a member is accessed, and use a lock to ensure that no other thread destroys or uses the singleton while the operation is in progress. However, the proxy does not directly disable the destroy member function upon creation and re-enable it upon destruction. This is generally unnecessary because calls by other threads to destroy will be locked, and the member function shouldn't be making a call to try to destroy itself.
Similarly, operator *() can return the same (or a similar) proxy thus keeping the object alive during its use. If that proxy provides a conversion operator (gasp!) to the object managed by the singleton smart pointer, then you get complete safety and access to the raw object.
The conversion operator (probably only appropriate on a second proxy type) make this possible:
A & object(proxy);
This is clever and would probably work. However, I have two questions: A) What benefits would a 'smart reference' provide that the smart pointer doesn't already? B) Would such benefits outweigh the fact that client code would now be capable of accidentally storing and using a real reference to the singleton? For instance: class MySingleton : public basic_singleton < MySingleton > { }; // client code class Foo { private: MySingleton & instance; public: Foo ( MySingleton::pointer ptr ) : instance ( *ptr ) { } };
In almost every circumstance, it is preferable for client code to use and pass around singleton::pointers rather than singleton *s, just like it is preferable for client code to use smart pointers to manage memory rather than trying to manage memory and raw pointers manually. Managing them manually can be done safely, but it is harder and error prone.
I understand the motivation for the original question. I'm just having a hard time getting you to see my point, it would seem. Hopefully, spelling out more of the details has helped.
I think I understand your idea now, please correct me if I am mistaken. -Jason

From: Jason Hise <chaos@ezequal.com>
Rob Stewart wrote:
From: Jason Hise <chaos@ezequal.com>
Note that I can write this:
class A { ... }; // whatever the proxy's name is: typedef boost::singleton<A>::some_proxy proxy_type; proxy_type proxy(s.operator ->()); proxy->whatever();
Actually, the proxy type (which is called access_pointer in the code) is a private member class of the pointer class, and thus variables of that type cannot be created or talked about by client code.
Good.
[a proxy type for operator *()] means I can write
A & object(*proxy);
or
A & object(proxy.get_object());
and then I can do what I like with object.
Ahh, I think I understand now. So essentially, pointer::operator * () would return a 'smart reference' class instance?
Yep.
Similarly, operator *() can return the same (or a similar) proxy thus keeping the object alive during its use. If that proxy provides a conversion operator (gasp!) to the object managed by the singleton smart pointer, then you get complete safety and access to the raw object.
The conversion operator (probably only appropriate on a second proxy type) make this possible:
s/make/makes/
A & object(proxy);
This is clever and would probably work. However, I have two questions:
A) What benefits would a 'smart reference' provide that the smart pointer doesn't already?
Safe access to the controlled object that you don't get with get_unsafe_ptr().
B) Would such benefits outweigh the fact that client code would now be capable of accidentally storing and using a real reference to the singleton? For instance:
If you provide get_unsafe_ptr(), the client can already do all of those things. What I'm suggesting adds safety. You can choose to provide what I'm describing via a member function other than operator *() just so it isn't so easy, if you like. Either way, you need to document the things one shouldn't do with that level of access to the object.
In almost every circumstance, it is preferable for client code to use and pass around singleton::pointers rather than singleton *s, just like it is preferable for client code to use smart pointers to manage memory rather than trying to manage memory and raw pointers manually. Managing them manually can be done safely, but it is harder and error prone.
I understand the motivation for the original question. I'm just having a hard time getting you to see my point, it would seem. Hopefully, spelling out more of the details has helped.
I think I understand your idea now, please correct me if I am mistaken.
By George, I think he's got it! -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
A & object(proxy);
This is clever and would probably work. However, I have two questions:
A) What benefits would a 'smart reference' provide that the smart pointer doesn't already?
Safe access to the controlled object that you don't get with get_unsafe_ptr().
But that's what the smart pointer itself already provides. In what circumstances would it be better for client code to work with a smart reference than with a smart pointer? The smart pointer provides access to singleton members via the -> operator. A smart reference would be unable to provide access to singleton members via the dot operator without first being cast, assigned, or copied into a reference. Why would client code prefer this: void Foo ( ) { MySingleton::reference r = *( MySingleton::pointer ( ) ); static_cast < MySingleton & > ( r ).DoSomething ( ); } to this? void Foo ( ) { MySingleton::pointer p; p->DoSomething ( ); } It seems to me that smart references would be too clumsy to work with, and this would encourage client code to store and use real references just to avoid all the casting. -Jason

From: Jason Hise <chaos@ezequal.com>
Rob Stewart wrote:
A & object(proxy);
This is clever and would probably work. However, I have two questions:
A) What benefits would a 'smart reference' provide that the smart pointer doesn't already?
Safe access to the controlled object that you don't get with get_unsafe_ptr().
But that's what the smart pointer itself already provides. In what circumstances would it be better for client code to work with a smart reference than with a smart pointer? The smart pointer provides access
The very one you started this thread with! You asked whether get_unsafe_ptr() should be spelled operator *(). You wanted to provide access to the raw pointer/reference in order to allow for polymorphic usage. The proxy to which I've been referring provides a safe means for that because the client can grab and hold a smart reference which can converts (explicitly or implicitly, your choice) to T & and T *.
to singleton members via the -> operator. A smart reference would be unable to provide access to singleton members via the dot operator without first being cast, assigned, or copied into a reference.
Why would client code prefer this: void Foo ( ) { MySingleton::reference r = *( MySingleton::pointer ( ) ); static_cast < MySingleton & > ( r ).DoSomething ( ); }
to this?
void Foo ( ) { MySingleton::pointer p; p->DoSomething ( ); }
That wasn't the purpose and of course you wouldn't choose the former.
It seems to me that smart references would be too clumsy to work with, and this would encourage client code to store and use real references just to avoid all the casting.
Are we back on track? -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
From: Jason Hise <chaos@ezequal.com>
But that's what the smart pointer itself already provides. In what circumstances would it be better for client code to work with a smart reference than with a smart pointer? The smart pointer provides access
The very one you started this thread with! You asked whether get_unsafe_ptr() should be spelled operator *().
You wanted to provide access to the raw pointer/reference in order to allow for polymorphic usage. The proxy to which I've been referring provides a safe means for that because the client can grab and hold a smart reference which can converts (explicitly or implicitly, your choice) to T & and T *.
I'm sorry, I figured out where the lapse in my thinking was. I was initially thinking that if the smart reference would be immediately converted to a real reference, then the smart reference would not serve a purpose and the function might as well return a real reference. If it was holding a lock, however, then it would add thread safety while the raw pointers or references existed. I need to get more sleep so I can think more clearly :) Your idea is indeed a good one. -Jason

From: Jason Hise <chaos@ezequal.com>
Rob Stewart wrote:
From: Jason Hise <chaos@ezequal.com>
But that's what the smart pointer itself already provides. In what circumstances would it be better for client code to work with a smart reference than with a smart pointer? The smart pointer provides access
The very one you started this thread with! You asked whether get_unsafe_ptr() should be spelled operator *().
You wanted to provide access to the raw pointer/reference in order to allow for polymorphic usage. The proxy to which I've been referring provides a safe means for that because the client can grab and hold a smart reference which can converts (explicitly or implicitly, your choice) to T & and T *.
I'm sorry, I figured out where the lapse in my thinking was. I was initially thinking that if the smart reference would be immediately converted to a real reference, then the smart reference would not serve a purpose and the function might as well return a real reference. If it was holding a lock, however, then it would add thread safety while the raw pointers or references existed.
Eureka! Yes, you've got it now. I'm glad we broke through the cognitive dissonance. 8-)
I need to get more sleep so I can think more clearly :) Your idea is indeed a good one.
Sleep? I have eight kids! I never get enough sleep! -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Thorsten Ottosen <nesotto <at> cs.auc.dk> writes:
"David Abrahams" <dave <at> boost-consulting.com> wrote in message news:uacnkw0cr.fsf <at> boost-consulting.com... | "Thorsten Ottosen" <nesotto <at> cs.auc.dk> writes: | > Pick up you're favourite book on OO and on STL and see if the | > programming techniques look alike. | | That's a very parsimonious explanation. It would be nice if you'd try | to make it a bit clearer in the context of the question of duplicating | objects, and show how it justifies a choice to prohibit ptr_containers | from acting like proper values.
the first reason must be its damn expensive without move-semantics.
So is std::vector<std::map<std::vector<std::string>, std::set<int> > >, std::vector<int> >. Should the standard library prevent users from using copy construct/copy assignment syntax with this data structure?
the second reason is that value-based and OO programming are different and are best kept seperate; there are different idioms, different parts of the langauge is being used.
How do you know ptr_containers will be used in OO contexts? Why can't a user use such containers to simply keep track of values? One of the great strengths of C++ is that it doesn't try to force the programmer into any one programming paradigm. Your justification for denying ordinary copy syntax is against this spirit. Bob

"Bob Bell" <belvis@pacbell.net> wrote in message news:loom.20050427T231515-202@post.gmane.org... | > the first reason must be its damn expensive without move-semantics. | | So is std::vector<std::map<std::vector<std::string>, std::set<int> > >, | std::vector<int> >. Should the standard library prevent users from using | copy construct/copy assignment syntax with this data structure? good question. I'm pretty sure that performace does prevent you from using such a beast; so why bother ask the question. | > the second reason is that value-based and OO programming are different and are | > best | > kept seperate; there are different idioms, different parts of the langauge is | > being used. | | How do you know ptr_containers will be used in OO contexts? Why can't a user | use such containers to simply keep track of values? he might; but the library is designed for use with OO; just like the "STL" is not really designed for OO. making it better at one thing, makes it worse at another thing; you can't have both; that's life. | One of the great strengths of C++ is that it doesn't try to force the | programmer into any one programming paradigm. Your justification for | denying ordinary copy syntax is against this spirit. so you would be against scoped_ptr being not copyable? besides, there is no direct connection between what C++ allows you and what a library written in C++ allows you. -Thorsten

Thorsten Ottosen <nesotto <at> cs.auc.dk> writes:
"Bob Bell" <belvis <at> pacbell.net> wrote in message news:loom.20050427T231515-202 <at> post.gmane.org...
| > the first reason must be its damn expensive without move-semantics. | | So is std::vector<std::map<std::vector<std::string>, std::set<int> > >, | std::vector<int> >. Should the standard library prevent users from using | copy construct/copy assignment syntax with this data structure?
good question. I'm pretty sure that performace does prevent you from using such a beast; so why bother ask the question.
Actually what prevents anyone from using it is that I got the braces wrong. ;-) Seriously, performance may or may not prevent me from using it, depending on what it's for and how I'll use it. But performance does not take away copy construction or copy assignment.
| > the second reason is that value-based and OO programming are different and are | > best | > kept seperate; there are different idioms, different parts of the langauge is | > being used. | | How do you know ptr_containers will be used in OO contexts? Why can't a user | use such containers to simply keep track of values?
he might; but the library is designed for use with OO; just like the "STL" is not really designed for OO. making it better at one thing, makes it worse at another thing; you can't have both; that's life.
I disagree; STL works just fine in OO contexts. Why do you think it doesn't?
| One of the great strengths of C++ is that it doesn't try to force the | programmer into any one programming paradigm. Your justification for | denying ordinary copy syntax is against this spirit.
so you would be against scoped_ptr being not copyable?
The absence of copying in scoped_ptr is not about enforcing or even encouraging a programming paradigm. Scoped_ptr denies copying because of what scoped _ptr _is_: a non-copyable type. The same argument cannot be made for ptr_container, for two reasons. First, ptr_container is a kind of container, and containers (as I understand them) are copyable. Second, you do allow copying anwyay, only through a non-standard API.
besides, there is no direct connection between what C++ allows you and what a library written in C++ allows you.
Not in any kind of iron-clad logic sort of way. But when I see a library that goes against the grain of the language's spirit (for lack of a better word), it makes me feel a little uncomfortable about that library. One thing I'm wondering about which I don't think I've seen discussed in this thread yet is: suppose you added copy operations to ptr_container. Who would be harmed? Before you say "anybody who uses those operations, because their performance is degraded," keep in mind that those people are also benefitting from those operations. I'm asking about people who are harmed without receiving any benefit. Bob

"Bob Bell" <belvis@pacbell.net> wrote in message news:loom.20050428T013921-284@post.gmane.org... | Thorsten Ottosen <nesotto <at> cs.auc.dk> writes: | Seriously, performance may or may not prevent me from using it, depending | on what it's for and how I'll use it. But performance does not take away | copy construction or copy assignment. as I said, I don't think the performance argument is the most important. | > | > the second reason is that value-based and OO programming are different and | > are | > | > best | > | > kept seperate; there are different idioms, different parts of the langauge | > is | > | > being used. | > | | > | How do you know ptr_containers will be used in OO contexts? Why can't a user | > | use such containers to simply keep track of values? | > | > he might; but the library is designed for use with OO; just like the "STL" | > is not really designed for OO. making it better at one thing, makes it worse | > at another thing; | > you can't have both; that's life. | | I disagree; STL works just fine in OO contexts. Why do you think it doesn't? there is nothing in there that has been developed with OO in mind. do you the qoute from Stepanov about OO? | > | One of the great strengths of C++ is that it doesn't try to force the | > | programmer into any one programming paradigm. Your justification for | > | denying ordinary copy syntax is against this spirit. | > | > so you would be against scoped_ptr being not copyable? | | The absence of copying in scoped_ptr is not about enforcing or even | encouraging a programming paradigm. Scoped_ptr denies copying because of | what scoped _ptr _is_: a non-copyable type. but we could have made it copyable just like you're trying to say we can make pointer containers copyable. at the bottom line, C++ copies too much and sometimes its just a benefit to be made aware of that fact | The same argument cannot be made for ptr_container, for two reasons. First, | ptr_container is a kind of container, ^^^^^^^^^^^^^^^^ | and containers (as I understand them) ^^^^^^^^^^^^^^ as you remark, pointer containers are "a kind of " containers; so they are not containers in the usual sense. | One thing I'm wondering about which I don't think I've seen discussed in | this thread yet is: suppose you added copy operations to ptr_container. | Who would be harmed? Before you say "anybody who uses those operations, | because their performance is degraded," keep in mind that those people are | also benefitting from those operations. I'm asking about people who are | harmed without receiving any benefit. you can't assume that all people are going to see your perceived benefits as their own benefits. making a type copyable or not affects how you design your program; I don't believe it is sound not to be conscious about cloning of class hierarchies. in 10 lines of code you can have a copyable ptr_vector if you want, I don't see why you can't just do that if you want that? -Thorsten

From: "Thorsten Ottosen" <nesotto@cs.auc.dk>
"Bob Bell" <belvis@pacbell.net> wrote in message news:loom.20050428T013921-284@post.gmane.org... | Thorsten Ottosen <nesotto <at> cs.auc.dk> writes:
| Seriously, performance may or may not prevent me from using it, depending | on what it's for and how I'll use it. But performance does not take away | copy construction or copy assignment.
as I said, I don't think the performance argument is the most important. [snip] | > | One of the great strengths of C++ is that it doesn't try to force the | > | programmer into any one programming paradigm. Your justification for | > | denying ordinary copy syntax is against this spirit. | > | > so you would be against scoped_ptr being not copyable? | | The absence of copying in scoped_ptr is not about enforcing or even | encouraging a programming paradigm. Scoped_ptr denies copying because of | what scoped _ptr _is_: a non-copyable type.
but we could have made it copyable just like you're trying to say we can make pointer containers copyable.
Are you seriously making that point? scoped_ptr wouldn't be scoped_ptr if it copied. A pointer container remains every bit a pointer container if it provides the usual spellings for copying and assignment.
at the bottom line, C++ copies too much and sometimes its just a benefit to be made aware of that fact
Why should one library assume the pedagogical role of teaching C++ programmers that C++ copies too much? If you're that concerned that people will copy pointer containers more than they realize, with resulting performance problems, and perhaps lead to abandoning the library, just add a manifest constant controlled diagnostic when copies occur.
| The same argument cannot be made for ptr_container, for two reasons. First, | ptr_container is a kind of container, ^^^^^^^^^^^^^^^^ | and containers (as I understand them) ^^^^^^^^^^^^^^
as you remark, pointer containers are "a kind of " containers; so they are not containers in the usual sense.
Why are you picking at nits like this? Yep, it's "a kind of" container, so one expects it to work like most every other container one encounters.
| One thing I'm wondering about which I don't think I've seen discussed in | this thread yet is: suppose you added copy operations to ptr_container. | Who would be harmed? Before you say "anybody who uses those operations, | because their performance is degraded," keep in mind that those people are | also benefitting from those operations. I'm asking about people who are | harmed without receiving any benefit.
you can't assume that all people are going to see your perceived benefits as their own benefits.
making a type copyable or not affects how you design your program; I don't believe it is sound not to be conscious about cloning of class hierarchies.
So instrument the operations that trouble you. You could offer an option to disable those operations, so long as those that want them can enable them.
in 10 lines of code you can have a copyable ptr_vector if you want, I don't see why you can't just do that if you want that?
Because then most users of the library will need to write that same code. That means it should be in the library. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

"Rob Stewart" <stewart@sig.com> wrote in message news:200504281321.j3SDLhm01683@vanzandt.balstatdev.susq.com... | From: "Thorsten Ottosen" <nesotto@cs.auc.dk> | > "Bob Bell" <belvis@pacbell.net> wrote in message | > | The absence of copying in scoped_ptr is not about enforcing or even | > | encouraging a programming paradigm. Scoped_ptr denies copying because of | > | what scoped _ptr _is_: a non-copyable type. | > | > but we could have made it copyable just like you're trying to say we can make | > pointer containers copyable. | | Are you seriously making that point? scoped_ptr wouldn't be | scoped_ptr if it copied. A pointer container remains every bit a | pointer container if it provides the usual spellings for copying | and assignment. apparently we disagree. | > at the bottom line, C++ copies too much and sometimes its just a benefit to be | > made aware | > of that fact | | Why should one library assume the pedagogical role of teaching | C++ programmers that C++ copies too much? If you're that | concerned that people will copy pointer containers more than they | realize, with resulting performance problems, and perhaps lead to | abandoning the library, just add a manifest constant controlled | diagnostic when copies occur. not sure what you by diagnostic. | > | The same argument cannot be made for ptr_container, for two reasons. First, | > | ptr_container is a kind of container, | > ^^^^^^^^^^^^^^^^ | > | and containers (as I understand them) | > ^^^^^^^^^^^^^^ | > | > as you remark, pointer containers are "a kind of " containers; so they are not | > containers in the usual sense. | | Why are you picking at nits like this? Yep, it's "a kind of" | container, so one expects it to work like most every other | container one encounters. its the nits that makes all the difference in our viewpoints. to me, this whole thread is a nit. | > in 10 lines of code you can have a copyable ptr_vector if you want, I don't | > see why you can't | > just do that if you want that? | | Because then most users of the library will need to write that | same code. I doubt that. -Thorsten

Rob Stewart <stewart@sig.com> writes:
in 10 lines of code you can have a copyable ptr_vector if you want, I don't see why you can't just do that if you want that?
Because then most users of the library will need to write that same code. That means it should be in the library.
I suggest we all drop it. I thought that overwhelming public opinion might be enough to make Thorsten think differently about this, but he seems to be committed to his position; I think it's a lost cause. Next time we will have to pay more attention at review time to what is getting approved and make sure our votes reflect our design sensibilities. Sorry for the turmoil, Dave -- Dave Abrahams Boost Consulting www.boost-consulting.com

Howard Hinnant <hinnant@twcny.rr.com> writes:
On Apr 26, 2005, at 12:08 PM, David Abrahams wrote:
I had this disagreement with Thorsten in Lillehammer (or was it Oxford?) but he was unimpressed with my arguments. I was wondering what Boost as a whole would think:
My Thesis: the library should support copying and assignment of pointer containers using the usual copy/assignment syntax, rather than only allowing a copy via a clone() function.
Rationale: Not being able to copy and assign pointer containers is an arbitrary restriction that limits usefulness and expressivity. The argument Thorsten gives against copyability is that the elements would have to be clone()d rather than copied, which is expensive because there's a dynamic allocation per element. I don't get it; we don't arbitrarily prohibit copying of std::vector<std::string> even though that incurs a dynamic allocation per element.
Example: Consider std::pair<int,some_pointer_container>. Almost nothing works. IIUC, it's not copyable, can't be initialized, and can't be clone()d.
Why not just a clone_ptr<T> which calls clone on copy construction / assignment? Then you could std::container<clone_ptr<T>>.
That makes insertion in a std::vector<clone_ptr<T> > expensive. But this is really beside my point.
On your thesis: The library should only support copying and assignment of pointer containers using the usual copy/assignment syntax if a non-destructive copy of the pointer can be made.
The pointers are just plain pointers, so it can.
The proposed std::container<unique_ptr<T>> won't be such a container. Having std::container<auto_ptr<T>> fail at compile time is still a good idea. Otherwise it would destructively copy with copy syntax.
This seems to be beside my point also.
<sigh> I realize these thoughts are a little ahead of their time at the moment due to the lack of compilers/libs which would make std::vector<clone_ptr<T>> acceptably efficient.
In preparing my notes for speaking about rvalue references at ACCU I realized that move semantics can be fully accomplished within the current language, if clumsily. The forwarding problem really makes new compilers _necessary_. Of course, move without forwarding is not really all that compelling anyway. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (12)
-
Aleksey Gurtovoy
-
Beman Dawes
-
Bob Bell
-
David Abrahams
-
Felipe Magno de Almeida
-
Gennadiy Rozental
-
Howard Hinnant
-
Jason Hise
-
Pavel Chikulaev
-
Peter Dimov
-
Rob Stewart
-
Thorsten Ottosen