Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The move function in unique_ptr C++03 emulation

I'm trying to understand how C++03 emulation of unique_ptr is implemented. unique_ptr is quite like std::auto_ptr but safer. It spits out compiler errors in cases where auto_ptr would have transferred ownership implicitly (i.e., silently). For example, a simple assignment. Function move is the key behind emulated unique_ptr's safety.

Questions:

  1. Why are there three move functions?
  2. The third move function that accepts a reference and turns it into an rvalue, is implemented (simplified) as follows.

    T move(T &t) { 
      return T(detail_unique_ptr::rv<T>(t)); 
    }
    

In the above code, an explicit conversion to T seems kind of unnecessary. In fact, Visual Studio 2010 is perfectly happy without an explicit conversion to T.

T move(T &t) {
  return detail_unique_ptr::rv<T>(t);
}

g++, clang, Comeau, however, do not like the second version. These compilers complain that there is no constructor for unique_ptr<T> that takes detail_unique_ptr::rv<T> as a parameter. Why is that? unique_ptr already defines a (non-explicit) constructor that takes detail_unique_ptr::rv<T> as a parameter. Why isn't that one picked up automatically?

like image 260
Sumant Avatar asked Dec 09 '11 18:12

Sumant


1 Answers

The reason is because you cannot initialize a unique_ptr with another unique_ptr without doing a user defined conversion (to rv, by passing the rvalue to the rv-taking constructor of unique_ptr). However when not explicitly calling the ctor of unique_ptr (as in unique_ptr(...)), you do a copy initialization which in your case first successfully constructs a rvalue temporary unique_ptr but then fails to copy that temporary into the return value target object, because in that copy, no user defined conversions are allowed (this is also known as the principle rule "no two user defined conversions in an initialization"). Msvc allows the copy to use the ctor taking a nonconst unique_ptr reference, which is nonstandard.

When doing copy initialization of a class from an object of the same class, there is no such two-step initialization. The source object is just passed to the non-explicit constructors of unique_ptr, which will convert it to rv using the rv-taking constructor, and by that way successfully construct the return value target object.

For the same reason, there is no implicit conversion from unique_ptr<Derived> to unique_ptr<Base>. In the first step, a unique_ptr<Base> will be successfully created, but then when copying that temporary into the unique_ptr<Base> target object, the restriction that no user defined conversions can be used prevents success.

like image 105
Johannes Schaub - litb Avatar answered Nov 15 '22 09:11

Johannes Schaub - litb