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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:19:10  来源:igfitidea点击:

creating a shared_ptr from unique_ptr

c++c++11g++smart-pointersclang++

提问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_ptrfrom std::unique_ptr:

在我最近查看的一段代码中,它用 编译得很好g++-4.6,我遇到了一个奇怪的尝试来创建一个std::shared_ptrfrom 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::moveis 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_ptrhasn't been moved from. The standard guaranteesthat it should be null after it has been moved from. The shared_ptrpoints 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_ptrwith the shared_ptrctor taking an rvalue ref to a unique_ptrinstead of make_shared:

如果我s_ptr使用shared_ptrctor 将右值引用改为 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_ptris null after the move as required by the standard and s_ptrpoints 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_sharedforwards 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_sharedis the wrong thing to use in this situation. shared_ptrhas 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 intand initialising it with an rvalue reference to std::unique_ptr<int>. For ints, that simply shouldn't compile.

这意味着它正在动态创建int并使用对 的右值引用对其进行初始化std::unique_ptr<int>。对于ints,那根本不应该编译。

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.

我已将此示例添加到错误报告中