std::move into static_pointer_cast: Why doesn't static_pointer_cast have an rvalue reference overload?
假设我们有一个功能,希望按值共享一个指针。 (在现实生活中的示例中,我通过右值引用来接受它,并将其转发给成员。)
1
| void f(std::shared_ptr<Derived> ptr) { ... } |
但是我们只有一个指向基类的共享指针,因此我们使用static_pointer_cast:
1 2
| std::shared_ptr<Base> ptr = std::make_shared<Derived>();
f(std::static_pointer_cast<Derived>(ptr)); |
第一次分配(从临时对象构造ptr)会触发原子计数的递增和递减计数,还是共享指针被移动? (请注意,它是向上广播的。)
在static_pointer_cast内,引用计数有一个原子增量。 如果我们不再需要ptr,我们希望将其移至f。 但是,由于没有static_pointer_cast的重载来获取右值引用,因此该移动不会产生任何效果:
1
| f(std::static_pointer_cast<Derived>(std::move(ptr))); |
一旦ptr被破坏,我们仍然有原子增量和相应的原子减量。 为什么没有这种过载?
-
推测:此处列出的"可能的实现":en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast都使用shared_ptr的构造函数,该构造函数不接受移动的参数。现在,为什么我没有告诉我为什么没有变动。
-
不能移动static_pointer_cast的结果吗?
-
@rubenvb static_pointer_cast的结果是一个临时值,因此是一个右值,因此它会自动移动。但是问题是您显然无法进入static_pointer_cast。
-
如何使用shared_ptr的转换"移动构造函数"?这里的10号。
-
@rubenvb不起作用,仅在指针进行隐式转换时才起作用,即不向下转换。
-
是的。确实。在阅读您的问题,我的第一条评论和写第二条评论之间,我忘记了这一点。似乎C ++ 17通过指定"兼容的指针类型"来减轻这种负担,还是真的更好? Cant现在似乎找到了这个定义。
-
@rubenvb似乎可以在C ++ 17中使用。
-
@ Creep4Play参考?无法确定没有动态强制转换就可以将基类指针安全地转换为派生指针。动态强制转换会冒着运行时错误的风险,这对于异构move构造函数是不允许的(它是noexcept,必须成功构造)。
-
@NirFriedman相同的参考:链接。我不知道C ++ 17草案,所以我不知道如果遇到失败,它将进行(不安全)静态强制转换还是(安全)动态强制转换。后者根本不是不可能的,因为:动态转换不会触发运行时错误。它将返回一个空指针。构造一个空的shared_ptr并没有错。仍然有移动版本的static_pointer_cast和dynamic_pointer_cast imo会更好,因为这样可以在安全和不安全之间进行选择。
-
@ Creep4Play链接明确表示,构造后*this包含参数的先前状态。这意味着除非参数为null,否则它不能为null。 C ++中的构造函数不应该那样失败,因为这样它们的发布条件将失效。例如:如果未引发异常,则auto a = b; auto c = std::move(b); assert(a == c)应该始终对所有常规类型均有效。
-
@ Creep4Play您仍然没有提供任何参考,实际上表明它可以与C ++ 17一起使用...我不认为这样,我不认为这是在这种情况下兼容的意思,这就是为什么我要求您提供指向的链接阐明为什么您认为它会在17年内起作用。
我可以回答您问题的第一部分,但不能回答第二部分。 虽然我不确定标准是否规定了它,但是我很确定:
1
| std::shared_ptr<Base> ptr = std::make_shared<Derived>(); |
不会做任何多余的refcounter增量/减量。 首先,让我观察到这实际上根本不是赋值,而是ptr的构造。 显然,它是由一个临时对象构造的,并且显然,该临时对象是另一种类型。 将被匹配的构造函数的签名是(http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr):
1 2
| template< class Y >
shared_ptr( shared_ptr<Y>&& r ); |
在注释中说:
Move-constructs a shared_ptr from r. After the construction, *this contains a copy of the previous state of r, r is empty and its stored pointer is null. The template overload doesn't participate in overload resolution if Y* is not implicitly convertible to T*
在这种情况下,Y是Derived,T是Base,因此很明显,我们得到了从Y*到T*的隐式转换,因此构造函数是合法的。 严格来说,允许引用计数首先增加到2,然后再减少到1可能符合要求。但是显然,这违反了move构造函数的全部目的,因此我非常怀疑这是如何实现的。