
On Dec 18, 2008, at 10:22 AM, Chris Newbold wrote:
From: boost-bounces@lists.boost.org [mailto:boost- bounces@lists.boost.org] On Behalf Of Howard Hinnant Sent: Wednesday, December 10, 2008 4:28 PM
Here's a sketch of what Sebastian is referring to. It isn't a complete unique_ptr. It is just a move-only-foundation to build off of.
...
We've got a number of use-cases where unique_ptr would be extremely helpful. So, I figured I'd take a stab at a reasonable emulation and see how far I could get. I started playing around with Howard's skeleton to convince myself that I understood enough about rvalue references and move semantics to be able to finish the implementation.
What I found, though, is that my understanding is lacking, particularly with regard to attempting to emulate the correct behavior in a C++03 world.
Given Howard's skeleton, I tried the following test case:
void f() { unique_ptr<int> p1(new int); unique_ptr<int> p2(p1); }
This of course does not compile because there is no publically accessible constructor for unique_ptr that can do the job. The conversions and constructors using 'rv' aren't accessible either.
In the end, I concluded that emulation on C++03 could provide move semantics, but that move operations need to be explicit, not implicit. That is, the test case should be:
void f() { unique_ptr<int> p1(new int); unique_ptr<int> p2(move(p1)) }
Is my conclusion correct?
Your conclusion is correct. And this is not a problem. moves from lvalues (p1) should be explicit, even in C++0X. Here the emulation is perfect. Note that if p1 is an rvalue, the move is implicit, both emulated and in C++0X: unique_ptr<int> g(); void f() { unique_ptr<int> p2(g()); // implicit move, good } One place the emulation breaks down is inside of g(): unique_ptr<int> g() { unique_ptr<int> p; // ... return move(p); // ! explicit move necessary for emulation, unwanted in C++0X } In C++0X one should say: return p; instead. Putting the "move" on the return statement will inhibit rvo, probably making your code slower. But in C++03 emulation, the move is required, else you get a compile time error. If you can build the return value in the return statement, then both C++03 emulation and C+ +0X are identical: unique_ptr<int> g() { return unique_ptr<int>(); // implicit move, good } Another currently weak place in the emulation is that "move" is injected as a friend of the move-only class. In C++0X, move is a std::lib function, not to be implemented by user code. And user code should always call std::move(x), not move(x). -Howard
-Chris
template <class T> class unique_ptr { T* ptr_;
unique_ptr(unique_ptr&); unique_ptr& operator=(unique_ptr&);
class rv { unique_ptr& r_; public: explicit rv(unique_ptr& r) : r_(r) {} unique_ptr* operator->() {return &r_;} };
public:
operator rv() {return rv(*this);} unique_ptr(rv r) : ptr_(r->release()) {} unique_ptr& operator=(rv r) {reset(r->release()); return *this;} friend unique_ptr move(unique_ptr& u) {return unique_ptr(rv(u));} friend unique_ptr move(rv r) {return unique_ptr(r);}
explicit unique_ptr(T* p = 0) : ptr_(p) {} template <class U> unique_ptr(unique_ptr<U> u) : ptr_(u.release()) {} ~unique_ptr() {reset();}
template <class U> unique_ptr& operator=(unique_ptr<U> u) {reset(u.release()); return *this;}
T* release() { T* tmp = ptr_; ptr_ = 0; return tmp; }
void reset(T* ptr = 0) { if (ptr_) delete ptr_; ptr_ = ptr; } };
It isn't a perfect emulation. But I think it is pretty close. And where it fails, it is not a dangerous failure like moving from a const.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost