By Sumant


2011-12-09 18:55:51 8 Comments

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?

1 comments

@Johannes Schaub - litb 2011-12-09 21:53:52

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.

@Sumant 2011-12-11 01:54:29

Thanks litb! Would you like to tell me why other two move functions are needed?

@spockwang 2015-12-18 07:49:29

Why not call unique_ptr<T>(detail_unique_ptr::rv<T>) directly to do the copy which requires no user-defined conversion?

Related Questions

Sponsored Content

11 Answered Questions

[SOLVED] What are move semantics?

5 Answered Questions

[SOLVED] Returning unique_ptr from functions

  • 2010-11-30 17:44:37
  • Praetorian
  • 120871 View
  • 287 Score
  • 5 Answer
  • Tags:   c++ c++11 unique-ptr

5 Answered Questions

[SOLVED] Passing unique_ptr to functions

3 Answered Questions

[SOLVED] unique_ptr behaviour while passing through function

0 Answered Questions

std::move required on return of std::unique_ptr for Intel but not GCC and Clang

1 Answered Questions

[SOLVED] Move assignable class containing vector<unique_ptr<T>>

1 Answered Questions

[SOLVED] Emulating explicit cast in C++03

  • 2014-03-02 16:43:55
  • Siler
  • 357 View
  • 4 Score
  • 1 Answer
  • Tags:   c++ c++11 c++03

3 Answered Questions

[SOLVED] So can unique_ptr be used safely in stl collections?

1 Answered Questions

Sponsored Content