30 comments

[ 4.0 ms ] story [ 71.5 ms ] thread
You still need to dereference the `value_ptr` a lot, right? Otherwise argument dependent lookup won't work for the wrapped value type.

I guess you could provide specializations for all the common candidates (begin, end, swap, size, empty, data, and whatever gets added in the future) in the "valuable" namespace, but you'll need to be pretty aggressive with adding new specializations as people find ones they need.

Also, it might be a good idea to support a Deleter template parameter and propagate it to the underlying `unique_ptr`. Some projects use that customization point for various purposes, like instrumentation of (de)allocation or grabbing certain kinds of objects from memory pools.

That's just a confusing name for boost::optional, right?
Not exactly as boost::optional would have infinite size for recursive types. You would need to combine it together with recursive_wrapper.
The idea of a value_ptr is something that's been around in C++ for quite some time, now. It usually goes by the name clone_ptr or copy_ptr. (Googling for either will yield several implementations.)

Looking at the author's code on GitHub, this implementation seems decent.

One thing to note about this implementation is that it doesn't address the other big reason for doing dynamic allocations: polymorphic types. Value semantics are preserved so faithfully that objects are sliced just as they would be if they weren't dynamically allocated.

Consider this example: https://gist.github.com/SeanCline/c81218e4c0208ccb871268aecd...

For similar reasons it seems inappropriate for PIMPL. The template indirectly calls `sizeof(...)` on the template type. When that type is not concrete (which is likely the case with PIMPL) the code will not compile. The `value_ptr_example_pimpl.hpp` example doesn't compile for me using GCC 5.4.
Well, that isn't a flaw in the implementation of value_ptr. The example, as provided, won't compile because the the copy constructor, assignment operator, and destructor are declared `= default` in the header. This causes any translation units that include value_ptr_example_pimpl.hpp to attempt to stamp out the default implementation, which does require knowing `sizeof(Foo::Pimpl)`.

Moving those methods to value_ptr_example_pimpl.cpp, just as you'd normally do for PIMPL makes the compiler happy.

This works for me in VS2017 but should work on any C++11 compliant compiler: https://gist.github.com/SeanCline/55d700d4fbf8cc1bdaeb44b547...

Not sure I like this pointer. Copying a pointer should not be a heavyweight operation. That is pretty well established.

I wish C++ had a unique_ptr without the ability to be null. But that does not seem possible given that moving from a pointer must give a valid object.

If you don't copy the resource then you are sharing it. This means you'll have reference semantics and every instance operates on the same resource. Value_ptr implements value semantics and so it behaves like primitives.
> If you don't copy the resource then you are sharing it.

I think their point is more that the copy has an arbitrary complexity, so while you think you're copying a smart pointer you're triggering a possibly huge cascade behind the scenes.

> This means you'll have reference semantics and every instance operates on the same resource

You could have an explicit copy operation on an otherwise move-semantic smart pointer. No sharing, and while you get the ability to copy stuff if necessary it's not potentially hidden behind every argument passing or assignation, it becomes a very deliberate operation.

All the standard says is that the state is valid. It would come at a space cost I guess though.
not_null style classes might be what you're after, e.g. https://github.com/Microsoft/GSL/blob/master/include/gsl/gsl.... Whan trying to store nullptr in it at runtime that will result in an exception or abort or whatever it's implemented like. Depending on the usecase, it's still better than manually checking for nullptr though.

moving from a pointer must give a valid object

Yes (well, actually it's state is 'valid but unspecified') but afaik performing any operations having preconditions on moved-from objects is undefined behaviour anyway so you shouldn't even be doing that. And when using not_null that would again result in an exception.

> Not sure I like this pointer. Copying a pointer should not be a heavyweight operation. That is pretty well established.

An alternative option would be a unique_ptr subtype (or extension) which allows cloning if the wrapped type is clonable, something similar to rust's

    impl<T> Clone for Box<T> where T: Clone
Can that be expressed without concepts?
Yes, but it is a bit awkward. You can can separate implementations depending on whether T has a particular member function. I need to look up how every time, though.
> You can can separate implementations depending on whether T has a particular member function.

Separate implementation, but not separate interface? The Rust bit means Box is "transparently" clonable, meaning it only implements the clone trait(/interface) if the value it wraps also does, trying to clone a Box<T> where T can not be cloned would be a compile-time error.

C++ "smart pointers" share with threads a common risk: most people using them should avoid using them.
I contend that most people that will cause trouble with smart pointers will also cause trouble with manual memory management, so your point is moot.
People not understanding manual memory management should not write C++ at all, in my opinion.
"Most people" using new/delete with raw pointers = memory leaks and crashes, guaranteed.

unique_ptr and shared_ptr are really simple and incredibly useful. If you're too scared to use them, you should not be using C++ at all.

Well, I'm using C++ since 1993, so go figure.
Since memory is copied, it's really not clear to me what the advantage is of using value_ptr over just using values.

Is it just about heap vs. stack?

I think its about recursive types/members, e.g.

    class Tree { Tree _left, _right; };
Won't compile because it has an infinite size. However

    class Tree { Tree *_left, *_right; };
Compiles, but the author contends that a more explicit description of the ownership semantics is better:

    class Tree { value_ptr<Tree> _left, _right; }
This is also why `value_ptr` is different than `optional`.
I think you have to say something about the copy semantics too, because if you ignore those, value_ptr would be synonymous with unique_ptr.

   class Tree { Tree *_left, *_right; };
   Tree y = x;
makes a shallow copy.

   class Tree { unique_ptr<Tree> _left, _right; };
   Tree y = x;
doesn't compile, but

   class Tree { value_ptr<Tree> _left, _right; };
   Tree y = x;
makes a deep copy.
With value-ptr's would traversing the tree require making copies of examined nodes as you search? What would the tree_search method look like?
(comment deleted)