
On 02/10/2013 10:27 AM, Vicente J. Botet Escriba wrote:
Le 10/02/13 03:04, Vicente J. Botet Escriba a écrit :
Le 08/02/13 16:16, Ralph Tandetzky a écrit :
Hi,
is there any interest in a copy-on-write pointer implementation? I wrote a cow_ptr<T> template class for C++11 which has the following use cases:
As others I would prefer another name for the class. Could you compare how you class relates to value_ptr as defined here (file://localhost/Users/viboes/Downloads/n3339-1.pdf)?
I meant " N3339: A Preliminary Proposal for a Deep-Copying Smart Pointer" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3339.pdf
Thank you for this awesome reference. That was very enlightening. The most obvious difference is that when value_ptrs are copied, then the pointee object is cloned instantly. There is no copy-on-write. But the value semantics are quite similar. Both value_ptr and cow_ptr support polymorphic cloning. I might use some ideas in value_ptr's source code like the automaticly choosing between default_clone and default_copy.
3. You can add cloning to a class hierarchy from the outside. With cow_ptr<Base> a( new Derived1 ); cow_ptr<Base> b( new Derived2 ); cow_ptr<Base> c; c = a; // performs a shallow copy. c->doSomething(); // makes a deep copy of a as a Derived1 class. // There is no slicing involved. you copy Base objects polymorphically. The class Base can even be abstract here. It is only required that Derived1 and Derived2 be CopyConstructible.
It seems to me that the copy of polymorphic object works only with the help of the user and that the following should be mentioned as a limitation
Base* ba = new Derived1; cow_ptr<Base> a( ba ); cow_ptr<Base> c; c = a; // performs a shallow copy. c->doSomething(); // couldn't makes a deep copy of a as a Derived1 class as the type has been lost. // There is a slicing involved.
In the DefaultCloner there is an assertion assert( typeid( *p ) == typeid( const T& ) ); which would be violated in the line you marked. So you'll notice that during debugging when the copy is performed. There's no way to check that at compile-time unfortunately.
If this is confirmed, I see it as a show-stopper, and the cloning strategy must be redefined.
I don't see any relational operators. Is this intentional?
To a degree. It's not totally obvious, if only the pointers will be compared, or if pointers are equal, if the pointed-to objects will be compared for equality as well. I'd prefer the first variant and I will add them in the next revision.
It is worth adding interactions with nullptr_t?
I never needed it. I'm not sure. Maybe for completeness sake. Should I add it?
What about a T* release() member function?
Rather not. Should this make a copy? It has to, if there's more than one copy. If there's only one copy, then it depends if there is an internal concrete_counter or a wrapping_counter. A wrapping counter wouldn't be able to release without copying. But the most compelling reason for me is that the caller wouldn't know for sure how to delete the object. The cow_ptr could have been initialized with some kind of special deleter. (It's kind of similar to shared_ptr.)
What about a reset(U*) member function ?
This stuff can be done with the constructors and the assignment operators. So, strictly speaking there's no need for it. You might get a bit better performance with reset() though. (Is performance actually the reason for shared_ptr and unique_ptr to provide a reset() function?) At first I wanted to keep the class interface simple. I might add it in the future. Are there compelling reasons to add it now?
Best, Vicente
Thank you for your great feedback!