我只是从Visual Studio 2013升级到2015年,并且我遇到了一系列问题,这些问题曾经在2013年开始工作,但在2015年却没有。
例如,这是一个让我难过的人。我用原始代码创建了一个测试用例。
基本上,代码通过std :: async()在一个线程中运行一些操作。在该线程中,可能抛出异常(A),该异常应放在std :: async()返回的future对象中。奇怪的是,在(B)中,Ex的析构函数被调用,但是对象仍然被抛出。在try-block中,当ex(D)变量离开得分时,如果' mInts' vector(X)是一个成员,程序会崩溃。如果我离开' mients'注释掉,如下所示,我仍然会有奇怪的行为。例如,这是用以下代码打印的内容:注意构造函数如何被调用,但析构函数被调用4次:
输出:
constructor
destructor
before exception
after exception
destructor
has exception
destructor
destructor
代码:
using FutureList = std::vector<std::future<void>>;
struct Ex {
Ex() {
std::cout << "constructor\n";
}
Ex(const Ex&) = delete;
Ex(Ex&&) {
std::cout << "move constructor";
}
~Ex() {
std::cout << "destructor\n";
}
void operator=(const Ex&) {
std::cout << "assign\n";
}
// std::vector<int> mInts; (X)
};
TEST(Explore, Test1) {
FutureList futures;
futures.push_back(
std::async(std::launch::async, []() {
throw Ex(); // (A)
}));
std::exception_ptr ex;
for (auto& i : futures) {
try {
i.get(); // (B)
std::cout << "Doesn't get here.\n";
}
catch (...) { // (C)
std::cout << "before exception\n";
ex = std::current_exception(); // (D)
std::cout << "after exception\n";
}
}
if (ex) {
std::cout << "has exception\n";
}
}
答案 0 :(得分:2)
实际上,MSVC不会在您的示例中调用复制构造函数。没有代码可以打电话;该功能已删除。它做得更糟:它将类视为易于复制并在对象上执行memcpy
。当你有std::vector<int>
类型的成员时,这就是崩溃的原因。
问题与std::async
没有直接关系。这是由std::exception_ptr
的实施引起的。 std::future
的共享状态使用exception_ptr
来存储异常,因此在异常上下文中使用future
将触发此问题。仅使用std::current_exception()
时可以复制该问题,其中涉及VC 14标准库实现中的副本(标准允许)。
问题在于__ExceptionPtr::_CallCopyCtor
中crt/src/stl/excptptr.cpp
的实施。它基本上做了一个测试,就像“是否有一个复制构造函数?不?很好,我们可以做memcpy
然后!”。
另一个问题是测试忽略了访问说明符。如果您提供复制构造函数但将其设为private
,则会调用它。
测试是在运行时完成的,所以没有机会出现编译时错误,不幸的是,这是唯一的方法:没有通用的编译时测试可以告诉{哪种类型的异常对象{在所有情况下都会指出{1}}。
Core issue 1863的拟议决议旨在通过要求
来避免此问题[...]为复制初始化选择的构造函数,将抛出的对象视为左值,应该是非删除和可访问的。 [...]
问题出在std::exception_ptr
状态,距离采用还有一步之遥。
因此,虽然这绝对是MSVC的一个问题,但它也与标准中的一个开放问题有关。现在,为异常对象提供复制构造函数似乎是个好主意,即使标准不需要它(还)。
更新:问题1863的解决方案已被纳入N4567工作草案,因此如果没有合适的构造函数,则需要执行更改的编译器拒绝代码。
答案 1 :(得分:1)
似乎MSVC 2015仍然会调用复制构造函数,即使它被标记为已删除。为了解决这个问题,我定义了复制构造函数。
打印输出的问题是因为复制构造函数中没有打印。我添加了一些,并且构造函数/析构函数计数匹配。
但是,MSVC 2015如果被标记为已删除,则不应该调用复制构造函数。如果必须调用它,那么它应该发出错误。