考虑以下代码:
#include <memory>
struct Foo { std::unique_ptr<Foo> next; };
void f(Foo &foo) { foo = std::move(*foo.next); }
int main() {
Foo foo{};
foo.next = std::make_unique<Foo>();
foo.next->next = std::make_unique<Foo>();
f(foo);
}
通过执行foo = std::move(*foo.next);
,foo.next.next
被移动到foo.next
如果foo.next
作为第一步无效,则可以立即删除它所指向的对象。这将导致删除foo.next.next
,也就是我试图移动到foo.next
的对象
我很确定我的推理中遗漏了什么,但我搞不清楚出了什么问题
这是一个安全的操作吗?标准在哪里让我放心?
我认为这一切都非常安全。当您在foo
上调用f()
函数时,class Foo
的移动赋值运算符将调用std::unique_ptr<Foo>::operator=(std::unique_ptr<Foo>&&)
。现在,C++14标准,§20.8.1.2.3,逗号2,说:
效果:将所有权从
u
转移到*this
,就像调用reset(u.release())
然后调用get_deleter() = std::forward<D>(u.get_deleter())
一样。
在§20.8.1.2.5,逗号4,我们发现reset()
:的行为
效果:将
p
分配给存储的指针,然后如果存储指针的旧值old_p
不等于nullptr
,则调用get_deleter()(old_p)
。[注意:这些操作的顺序很重要,因为对get_deleter()
的调用可能会破坏*this
。--结束注释]
因此,我们可以争辩说,存储的指针将被替换,然后旧存储的指针将按此顺序被删除。因此,一切都很好,定义得很好。
此外,当您将输入到reset()
函数中时,*foo.next
对象将已经是release()
d,因此指向的对象不会被它破坏。