C++ 从 unique_ptr 创建一个 shared_ptr
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18889843/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
creating a shared_ptr from unique_ptr
提问by stefan
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
:
在我最近查看的一段代码中,它用 编译得很好g++-4.6
,我遇到了一个奇怪的尝试来创建一个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).
这对我来说似乎很奇怪。这应该是std::shared_ptr<Foo>(std::move(foo));
afaik,尽管我对动作并不完全熟悉(而且我知道std::move
这只是一个演员,没有任何东西被感动)。
Checking with different compilers on this SSC(NUC*)E
在此 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
- g++-4.4.7 给出编译错误
- g++-4.6.4 编译没有任何错误
- g++-4.7.3 给出内部编译器错误
- g++-4.8.1 给出编译错误
- clang++-3.2.1 编译没有任何错误
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));
我们已经同意,其中一些编译器(例如 clang++ 和 g++-4.6.4)允许进行转换,而它们不应该这样做。但是,使用 g++-4.7.3(在 上产生内部编译器错误std::make_shared<Foo>(std::move(foo));
),正确拒绝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));
.
由于行为上的这种巨大差异,我将问题保持原样,尽管部分问题可以通过减少到int bar(std::move(foo));
.
*) NUC: Not universally compilable
*) NUC:不能普遍编译
采纳答案by Ali
UPDATE 2:This bughas been fixed in Clang in r191150. GCC rejects the code with a proper error message.
更新 2:此错误已在 r191150 中的 Clang 中修复。GCC 以正确的错误消息拒绝该代码。
UPDATE:I have submitted a bug report. The following code on my machine with clang++ 3.4 (trunk 191037)
更新:我已经提交了一个错误报告。以下代码在我的机器上使用 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
hasn't been moved from. The standard guaranteesthat it should be null after it has been moved from. The shared_ptr
points to a wrong value.
如您所见,unique_ptr
还没有被移出。该标准保证它在移出后应该为空。该shared_ptr
指向一个错误的值。
The weird thing is that it compiles without a warning and valgrind doesn't report any issues, no leak, no heap corruption. Weird.
奇怪的是它编译时没有警告,valgrind 没有报告任何问题,没有泄漏,没有堆损坏。奇怪的。
The proper behavior is shown if I create s_ptr
with the shared_ptr
ctor taking an rvalue ref to a unique_ptr
instead of make_shared
:
如果我s_ptr
使用shared_ptr
ctor 将右值引用改为 aunique_ptr
而不是创建,则会显示正确的行为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;
}
It prints:
它打印:
u_ptr.get() = 0x5a06040
*u_ptr = 42
After move
u_ptr.get() = 0
s_ptr.get() = 0x5a06040
*s_ptr = 42
As you see, u_ptr
is null after the move as required by the standard and s_ptr
points to the correct value. This is the correct behavior.
如您所见,u_ptr
按照标准的要求在移动后为空并s_ptr
指向正确的值。这是正确的行为。
(The original answer.)
(原答案。)
As Simple has pointed out: "Unless Foo has a constructor that takes a std::unique_ptr it shouldn't compile."
正如 Simple 指出的那样:“除非 Foo 有一个采用 std::unique_ptr 的构造函数,否则它不应该编译。”
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 that unique_ptr<T>&&
it is a compile error.
稍微扩展一下:make_shared
将其参数转发给 T 的构造函数。如果 T 没有任何可以接受unique_ptr<T>&&
它是编译错误的构造函数。
However, it is easy to fix this code 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 point is: make_shared
is the wrong thing to use in this situation. shared_ptr
has a ctor that accepts an unique_ptr<Y,Deleter>&&
, see (13) at the ctors of shared_ptr
.
关键是:make_shared
在这种情况下使用是错误的。shared_ptr
有一个 ctor 接受unique_ptr<Y,Deleter>&&
,参见(13) at the ctors ofshared_ptr
。
回答by Angew is no longer proud of SO
This shouldn't compile. If we disregard the uniqueness and sharedness of the pointers for a moment, it's basically trying to do this:
这不应该编译。如果我们暂时忽略指针的唯一性和共享性,它基本上是在尝试这样做:
int *u = new int;
int *s = new int(std::move(u));
It means it's dynamicallycreating an int
and initialising it with an rvalue reference to std::unique_ptr<int>
. For int
s, that simply shouldn't compile.
这意味着它正在动态创建int
并使用对 的右值引用对其进行初始化std::unique_ptr<int>
。对于int
s,那根本不应该编译。
For a general class Foo
, it depends on the class. If it has a constructor taking a 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 shouldn't compile.
对于一般班级Foo
,这取决于班级。如果它有一个采用std::unique_ptr<Foo>
by 值、const ref 或 rvalue ref的构造函数,它将起作用(但可能不会按照作者的意图行事)。在其他情况下,它不应该编译。
回答by Jonathan Wakely
Here's a reduced example which clang incorrectly compiles:
这是一个减少的示例,它会错误地编译:
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 the int
(and the Intel compiler does too.)
Clang 使用显式 bool 转换运算符来初始化int
(英特尔编译器也是如此。)
Clang 3.4 does not allow:
Clang 3.4 不允许:
int i = int(u);
but it does allow:
但它确实允许:
int* p = new int(u);
I think both should be rejected. (Clang 3.3 and ICC allow both.)
我认为两者都应该被拒绝。(Clang 3.3 和 ICC 都允许。)
I've added this example to the bug report.
我已将此示例添加到错误报告中。