In a piece of code I reviewed lately, which compiled fine with g++-4.6
, I encountered a strange try to create a std::shared_ptr
from std::unique_ptr
:
std::unique_ptr<Foo> foo...
std::make_shared<Foo>(std::move(foo));
This seems rather odd to me. This should be std::shared_ptr<Foo>(std::move(foo));
afaik, though I'm not perfectly familiar with moves (and I know std::move
is only a cast, nothing get's moved).
Checking with 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));
}
Results of compilation:
- g++-4.4.7 gives compilation error
- g++-4.6.4 compiles without any error
- g++-4.7.3 gives internal compiler error
- g++-4.8.1 gives compilation error
- clang++-3.2.1 compiles without any error
So the question is: which compiler is right in terms of the standard? Does the standard require this to be an invalid statement, a valid statement or is this simply undefined?
Addition
We've agreed on that some of these compilers, such as clang++ and g++-4.6.4, permit the conversion while they shouldn't. However with g++-4.7.3 (which produces an internal compiler error on std::make_shared<Foo>(std::move(foo));
), correctly rejects int bar(std::move(foo));
Because of this huge difference in behavior, I'm leaving the question as it is, although part of it would be answerable with the reduction to int bar(std::move(foo));
.
*) NUC: Not universally compilable
UPDATE 2: This bug has been fixed in Clang in r191150. GCC rejects the code with a proper error message.
UPDATE: I have submitted a bug report. The following code on my machine with clang++ 3.4 (trunk 191037)
prints this:
As you can see, the
unique_ptr
hasn't been moved from. The standard guarantees that it should be null after it has been moved from. Theshared_ptr
points to a wrong value.The weird thing is that it compiles without a warning and valgrind doesn't report any issues, no leak, no heap corruption. Weird.
The proper behavior is shown if I create
s_ptr
with theshared_ptr
ctor taking an rvalue ref to aunique_ptr
instead ofmake_shared
:It prints:
As you see,
u_ptr
is null after the move as required by the standard ands_ptr
points to the correct value. This is the correct behavior.(The original answer.)
As Simple has pointed out: "Unless Foo has a constructor that takes a std::unique_ptr it shouldn't compile."
To expand on it a little bit:
make_shared
forwards its arguments to T's constructor. If T doesn't have any ctor that could accept thatunique_ptr<T>&&
it is a compile error.However, it is easy to fix this code and get what you want (online demo):
The point is:
make_shared
is the wrong thing to use in this situation.shared_ptr
has a ctor that accepts anunique_ptr<Y,Deleter>&&
, see (13) at the ctors ofshared_ptr
.Here's a reduced example which clang incorrectly compiles:
Clang uses the explicit bool conversion operator to initialize the
int
(and the Intel compiler does too.)Clang 3.4 does not allow:
but it does allow:
I think both should be rejected. (Clang 3.3 and ICC allow both.)
I've added this example to the bug report.
This shouldn't compile. If we disregard the uniqueness and sharedness of the pointers for a moment, it's basically trying to do this:
It means it's dynamically creating an
int
and initialising it with an rvalue reference tostd::unique_ptr<int>
. Forint
s, that simply shouldn't compile.For a general class
Foo
, it depends on the class. If it has a constructor taking astd::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 shouldn't compile.