#include <memory>
#include <vector>
using namespace std;
vector<unique_ptr<int>> e;
void f(unique_ptr<int> u) {
e.emplace_back(move(u));
}f(std::unique_ptr<int, std::default_delete<int> >):
mov rsi, QWORD PTR e[rip+8] # rsi: vector.end_ptr
cmp rsi, QWORD PTR e[rip+16] # [e + rip + 16]: vector.storage_end_ptr
je .L52 # Slow path, need to reallocate
mov rax, QWORD PTR [rdi] # rax: unique_ptr<int> u
add rsi, 8 # end_ptr += 8
mov QWORD PTR [rdi], 0 # <==== Do we need to set the argument u to null here?
mov QWORD PTR [rsi-8], rax # *(end_ptr - 8) = u
mov QWORD PTR e[rip+8], rsi # update end_ptr
ret
.L52: # omitted我想知道为什么编译器会在这个函数中生成mov QWORD PTR[rdi], 0?是否有任何要求编译器这样做的约定?
此外,对于更简单的情况,如这
void f(unique_ptr<int> u);
void h(int x) {
auto p = make_unique<int>(x);
f(move(p));
}为什么编译器会生成:
call operator delete(void*, unsigned long)在h()的末尾,假设在调用f之后p总是nullptr
发布于 2018-11-16 02:41:45
在这两种情况下,答案都是:因为您移动的对象仍将被销毁。
如果您查看为调用
void f(unique_ptr<int> u);您将注意到,调用方为参数u创建对象,然后按照调用约定的要求调用其析构函数。如果对f()的调用是内联的,编译器很可能会优化它。但是为f()生成的代码无法控制u的析构函数,因此必须将u的内部指针设置为零,前提是u的析构函数将在函数返回后运行。
在第二个例子中,我们有一些相反的情况:
void h(int x) {
auto p = make_unique<int>(x);
f(move(p));
}与名称可能相反的是,std::move()实际上并不移动对象。它所做的就是转换为一个rvalue引用,它允许引用的接收方从所引用的对象中移动--如果他这样选择的话。实际的移动只会发生,例如,当另一个对象通过移动构造函数从给定的参数构造时。由于编译器在定义f()时不知道h()内部发生了什么,所以不能假设f()总是从给定的对象移动。例如,f()只能在某些情况下返回或移动,而在其他情况下则不能。因此,编译器必须假设函数可能返回而不移动对象,并且必须为析构函数发出delete。该函数还可以执行移动分配,而不是移动构造,在这种情况下,仍然需要外部析构函数来释放以前由新对象…的指定所有权所持有的对象的所有权。
https://stackoverflow.com/questions/53330428
复制相似问题