vla*_*don 18 c++ visual-c++ language-lawyer
这个程序:
#include <iostream>
using namespace std;
struct B {
B() { cout << "B"; }
//B(const B& b) { cout << "copyB"; }
~B() { cout << "~B"; }
};
struct C : B {
};
void f(B b) {
}
int main() {
C c;
f(c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出B~B~B~B,即三次调用析构函数,为什么?
仅在 MSVC 中。Clang 和 GCC 输出B~B~B(最有可能是正确的)。
有趣的是:如果你取消对 copy-ctor 的注释,它会输出BcopyB~B~B,这是正确的(析构函数调用了两次)。
它是 MSVC 编译器中的错误吗?或者这是正确的行为?
(Visual Studio 2019 最新,cl.exe版本 19.28.29337)
Ayx*_*xan 10
如果打印地址:
#include <stdio.h>
struct B {
B() { printf(" B() <%p>\n", (void*)this); }
~B() { printf("~B() <%p>\n", (void*)this); }
};
struct C : B { };
void f(B b) { }
int main() {
C c;
f(c);
}
Run Code Online (Sandbox Code Playgroud)
输出是:
B() <000000A013FFFAC4>
~B() <000000A013FFFAA0>
~B() <000000A013FFFBA4>
~B() <000000A013FFFAC4>
Run Code Online (Sandbox Code Playgroud)
如您所见,没有对象被破坏 2 次。似乎有一个临时的参与,据我所知,这是不允许的。这让我相信这是一个错误。这只发生在复制构造函数是微不足道的时候。由于析构函数不是微不足道的,它是可观察的行为,不在 as-if 规则中。
结论是这是MSVC的一个bug。尽管标准指定在将参数传递给函数时可以创建临时对象,但是,应该遵守许多限制。相关规则写如下:
[class.temporary#3]
当类类型 X 的对象传递给函数或从函数返回时,如果 X 至少有一个合格的复制或移动构造函数([特殊]),则每个此类构造函数都是 trivial,并且X 的析构函数是 trivial 或业已删除,允许实现创建临时对象来保存函数参数或结果对象。临时对象分别从函数参数或返回值构造,并且函数的参数或返回对象被初始化,就像使用合格的普通构造函数来复制临时对象一样(即使该构造函数不可访问或不会被重载选择)执行对象的复制或移动的分辨率)。
在您的示例中,传递的参数的类型为 class C,它派生自基类,B该基类具有非用户提供的复制构造函数和用户提供的析构函数,这将导致派生类C可以具有简单的复制构造函数,但不能具有根据[class.copy.ctor#11.2]和[class.dtor#8.2]有一个简单的析构函数,它们是以下规则:
如果类 X 的复制/移动构造函数不是用户提供的并且满足以下条件,则它是微不足道的:
- 选择复制/移动每个直接基类子对象的构造函数是微不足道的
如果析构函数不是用户提供的并且满足以下条件,则它是微不足道的:
- 该类的所有直接基类都有简单的析构函数
类不会满足所有这些限制C,因此在这种情况下无法创建临时对象。这意味着应该使用复制构造函数从参数初始化参数。这两个具有自动存储期限的对象将分别在块退出时被销毁。也就是说,~B这里只应打印两次。
| 归档时间: |
|
| 查看次数: |
315 次 |
| 最近记录: |