
On Mon, Feb 11, 2013 at 1:47 PM, Peter Dimov <lists@pdimov.com> wrote:
read_ptr<T> pr; if( write_ptr<T> pw = pr.write() ) { pw->mutate(); pr = std::move( pw ); }
I don't, however, particularly like the semantics of the case in which pr is already unique. In fact, now that I think of it, .write should be a move, too.
In the case that pr has a use_count of 1, definitely. I thought that's what you had been proposing all along :) Deep copy the pointed-to object only if the use_count >= 2. Whatever you get back, do your mutations, then pass it back to the read_ptr. If you have two pointers, then it should be move semantics, I agree. However, the above code is not strongly exception-safe. You would have to write read_ptr<T> pr; if ( write_ptr<T> pw = pr.write() ) { try { pr->mutate(); // might throw } catch (...) { pr = std::move( pw ); // no-throw throw; } pr = std::move( pw ); // no-throw } In order to fix it. (Or some scope_guard stuff, if you like.) The other
On 02/11/2013 11:34 PM, Jeffrey Lee Hellrung, Jr. wrote: thing: It it not very clear at which point the cloning happens, if it is necessary. There are 2 possibilities: 1. pr.write() does it. Or, if you want make it std::move(pr). 2. pw.operator->() does it. In case 1 using std::move() would be bad, because moving should normally be cheap and preferably not throw. Therefore I would prefer to use pr.write(). In case 2 the pointer types probably have to be friends and they are more tightly coupled. That isn't necessarily bad, because they are shipped together. I would prefer the variant 2. The two-pointer-solution makes it harder to write exception-safe code. Therefore I would prefer a single pointer type solution.
In this case, it's wouldn't be surprising to sometimes find pr holding a NULL after the move.
write_ptr<T> pw = std::move( pr ); pw->mutate(); pr - std::move( pw );
The advantage here is that (1) in
read_ptr<T> get_value(); write_ptr<T> p = get_value();
you don't need a write() call, I would expect most of the time you're not going to be immediately modifying a read_ptr rvalue like above. The typical use case I'd expect is you have existing read_ptr lvalue which points to an object you want to mutate. So whether you write .write() or move(pr) is not a huge syntactic difference. and (2) this avoids the mistake of doing
pr.write();
without storing the return value and as a result, losing *pr when it's unique. Just doing
std::move( pr );
would do nothing, so it's safer.
That's a fair point, but I think this is really an abuse of rvalue reference syntax :) I guess one concern I have is if you're moving a smart ptr, I'd expect it to be as fast as a couple pointer assignments, but here it could incur a deep copy, and that behavior is entirely runtime-dependent.
- Jeff Not if you do the work in pw.operator->() as pointed out above.
Thank you for your ideas, Ralph