在以下代码中(使用-std=c++14 -Wall -fno-elide-constructors
在gcc 9.2上构建:
struct Noisy {
Noisy() { std::cout << "Default construct [" << (void*)this << "]\n"; }
Noisy(const Noisy&) { std::cout << "Copy construct [" << (void*)this << "]\n"; }
Noisy(Noisy&&) { std::cout << "Move construct [" << (void*)this << "]\n"; }
Noisy& operator=(const Noisy&) { std::cout << "Copy assignment" << std::endl; return *this; }
Noisy& operator=(Noisy&&) { std::cout << "Move assignment" << std::endl; return *this; }
~Noisy() { std::cout << "Destructor [" << (void*)this << "]\n"; }
};
Noisy f() {
Noisy x;
return x;
}
Noisy g(Noisy y) {
return y;
}
int main(void) {
Noisy a;
std::cout << "--- f() ---\n";
Noisy b = f();
std::cout << "b [" << (void*)&b << "]\n";
std::cout << "--- g(a) ---\n";
Noisy c = g(a);
std::cout << "c [" << (void*)&c << "]\n";
std::cout << "---\n";
return 0;
}
哪个会产生此结果:
Default construct [0x7ffc4445737a]
--- f() ---
Default construct [0x7ffc4445735f]
Move construct [0x7ffc4445737c]
Destructor [0x7ffc4445735f]
Move construct [0x7ffc4445737b]
Destructor [0x7ffc4445737c]
b [0x7ffc4445737b]
--- g(a) ---
Copy construct [0x7ffc4445737e]
Move construct [0x7ffc4445737f]
Move construct [0x7ffc4445737d]
Destructor [0x7ffc4445737f]
Destructor [0x7ffc4445737e]
c [0x7ffc4445737d]
---
Destructor [0x7ffc4445737d]
Destructor [0x7ffc4445737b]
Destructor [0x7ffc4445737a]
为什么[0x7ffc4445735f]
中的本地噪声对象f()
的副本在移入f
的返回地址后(以及b
的构造开始之前)立即被销毁;而g()
似乎没有发生同样的事情吗?即在后一种情况下(执行g()
时),只有在准备好构造好Noisy y
之后,函数参数[0x7ffc4445737e]
,c
的本地副本才会被销毁。它不应该在移到g
的寄信人地址后立即被销毁吗?
这些是输出中地址的变量:
f()
我将问题解释为:您强调以下两点:
0x7ffc4445737a a
0x7ffc4445735f x
0x7ffc4445737c return value of f()
0x7ffc4445737b b
0x7ffc4445737e y
0x7ffc4445737f return value of g()
0x7ffc4445737d c
在构建x
之前被销毁b
构建完成后销毁]并询问为什么两种情况的表现都不一样。
答案是:在C ++ 14中,在[expr.call] / 4中指定的标准是在函数返回时应该销毁y
。但是,有些编译器不符合此要求,因此提出了缺陷报告。从C ++ 17开始,现在规范是由实现定义的,是在函数返回时还是在包含函数调用的完整表达式的末尾销毁c
。
您的编译器采用后一种选项。作为缺陷,这意味着可以考虑追溯应用,因此即使使用y
,编译器也可以正确应用缺陷的解决方案。