Creating shared_ptr from unique_ptr

In a piece of code that I recently reviewed that compiled with g++-4.6 , I came across a strange attempt to create std::shared_ptr from std::unique_ptr :

 std::unique_ptr<Foo> foo... std::make_shared<Foo>(std::move(foo)); 

This seems rather strange to me. It should be std::shared_ptr<Foo>(std::move(foo)); afaik, although I am not completely familiar with the moves (and I know that std::move is just a cast, nothing moves).

Validation using different compilers on this SSC (NUC *) E

 #include <memory> int main() { std::unique_ptr<int> foo(new int); std::make_shared<int>(std::move(foo)); } 

Compilation Results:

  • g ++ - 4.4.7 gives a compilation error
  • g ++ - 4.6.4 compilation without errors
  • g ++ - 4.7.3 gives an internal compiler error
  • g ++ - 4.8.1 gives a compilation error
  • clang ++ - 3.2.1 compiles without errors

So the question is: which compiler is right from the point of view of the standard? Is this requirement by default an invalid statement, a valid statement, or is it just undefined?

Adding

We agreed that some of these compilers, such as clang ++ and g ++ - 4.6.4, allow conversion until they should. However, with g ++ - 4.7.3 (which creates an internal compiler error on std::make_shared<Foo>(std::move(foo)); ), correctly rejects int bar(std::move(foo));

Due to this huge difference in behavior, I leave the question as it is, although part of it will be responsible for shortening to int bar(std::move(foo)); .




*) NUC: not universally compiled

+43
c ++ c ++ 11 smart-pointers g ++ clang ++
Sep 19 '13 at 8:34 on
source share
3 answers

UPDATE 2: This bug has been fixed in Clang in r191150. GCC rejects the code with the correct error message.




UPDATE: I sent a bug report. The following code on my machine with clang ++ 3.4 (trunk 191037)

 #include <iostream> #include <memory> int main() { std::unique_ptr<int> u_ptr(new int(42)); std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl; std::cout << "*u_ptr = " << *u_ptr << std::endl; auto s_ptr = std::make_shared<int>(std::move(u_ptr)); std::cout << "After move" << std::endl; std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl; std::cout << "*u_ptr = " << *u_ptr << std::endl; std::cout << " s_ptr.get() = " << s_ptr.get() << std::endl; std::cout << "*s_ptr = " << *s_ptr << std::endl; } 

prints this:

  u_ptr.get() = 0x16fa010 *u_ptr = 42 After move u_ptr.get() = 0x16fa010 *u_ptr = 42 s_ptr.get() = 0x16fa048 *s_ptr = 1 

As you can see, the unique_ptr element unique_ptr not been moved. The standard guarantees that it must be empty after it is moved. shared_ptr points to an invalid value.

It is strange that it compiles without warning, and valgrind does not report any problems, leak, lack of heap. Weird

The correct behavior is shown if I create s_ptr with shared_ptr ctor, taking rvalue ref instead of unique_ptr instead of make_shared :

 #include <iostream> #include <memory> int main() { std::unique_ptr<int> u_ptr(new int(42)); std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl; std::cout << "*u_ptr = " << *u_ptr << std::endl; std::shared_ptr<int> s_ptr{std::move(u_ptr)}; std::cout << "After move" << std::endl; std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl; //std::cout << "*u_ptr = " << *u_ptr << std::endl; // <-- would give a segfault std::cout << " s_ptr.get() = " << s_ptr.get() << std::endl; std::cout << "*s_ptr = " << *s_ptr << std::endl; } 

He prints:

  u_ptr.get() = 0x5a06040 *u_ptr = 42 After move u_ptr.get() = 0 s_ptr.get() = 0x5a06040 *s_ptr = 42 

As you can see, u_ptr is null after the move, as required by the standard, and s_ptr indicates the correct value. This is the correct behavior.




(original answer.)

He simply pointed out : "If Foo does not have a constructor that accepts std :: unique_ptr, it should not compile."

To expand it a bit: make_shared its arguments to the constructor of T. If T has no ctor that could accept that unique_ptr<T>&& is a compilation error.

However, this code can be easily fixed and get what you want ( online demo ):

 #include <memory> using namespace std; class widget { }; int main() { unique_ptr<widget> uptr{new widget}; shared_ptr<widget> sptr(std::move(uptr)); } 

The fact is that make_shared is the wrong thing to use in this situation. shared_ptr has a ctor that accepts unique_ptr<Y,Deleter>&& , see (13) in ctors shared_ptr .

+27
Sep 19 '13 at 14:35
source share

This should not compile. If we neglect the uniqueness and divisibility of momentary pointers, it basically tries to do this:

 int *u = new int; int *s = new int(std::move(u)); 

This means that it dynamically creates an int and initializes it with an rvalue reference to std::unique_ptr<int> . For int s, this simply should not compile.

For a general Foo class, it depends on the class. If it has a constructor that takes the value std::unique_ptr<Foo> by value, const ref or rvalue ref, it will work (but maybe not do what the author intended). In other cases, it should not be compiled.

+11
Sep 19 '13 at 8:41
source share

Here is an example that clang compiles incorrectly:

 struct ptr { int* p; explicit operator bool() const { return p != nullptr; } }; int main() { ptr u{}; int* p = new int(u); } 

Clang uses the explicit bool conversion operator to initialize int (and the Intel compiler does this too.)

Clang 3.4 does not allow:

 int i = int(u); 

but it allows you to:

 int* p = new int(u); 

I think both should be rejected. (Clang 3.3 and ICC allow both.)

I added this example to the report.

+5
Sep 19 '13 at 22:15
source share



All Articles