Weird behavior with exceptions thrown from std::async, in MSVC 2015
I just upgrade from Visual Studio 2013 to 2015, and I'm running into a bunch of issues with things that used to work in 2013, but which do not in 2015.
For example, here's one that has me stumped. I created a test-case out of the original code.
Basically, the code runs some operations in a thread, via std::async(). Within the thread, exceptions might be thrown (A), which should be placed in the future object returned by std::async(). The weird thing is that in (B), the destructor of Ex is called, but the object is still thrown aftewards. In the try-block, when the ex (D) variable leaves the score, if 'mInts' vector (X) is a member, the program would crash. If I leave 'mInts' commented out, as below, I still get weird behavior. For example, this is what's printed with the code below: notice how the constructor is called one, but the destructor is called 4 times:
Output:
constructor    
destructor   
before exception   
after exception  
destructor   
has exception   
destructor   
destructor
Code:
using FutureList = std::vector<std::future<void>>;
struct Ex {
  Ex() {
    std::cout << "constructorn";
  }
  Ex(const Ex&) = delete;
  Ex(Ex&&) {
    std::cout << "move constructor";
  }
 ~Ex() {
    std::cout << "destructorn";
  }
  void operator=(const Ex&) {
    std::cout << "assignn";
  }
// 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 exceptionn";
        ex = std::current_exception();    // (D)
        std::cout << "after exceptionn";
    }
  }
  if (ex) {
    std::cout << "has exceptionn";
  }
}
 Actually, MSVC doesn't call the copy constructor in your example.  There's no code to call;  the function is deleted.  It does something worse: it treats the class as being trivially copyable and does a memcpy on the object.  That's the reason for the crash when you have a member of type std::vector<int> .  
 The problem is not directly related to std::async .  It's caused by the implementation of std::exception_ptr .  The shared state of a std::future uses an exception_ptr to store an exception, so using a future in the context of exceptions will trigger the problem.  The issue can otherwise be reproduced when using only std::current_exception() , which involves a copy in VC 14's standard library implementation (the Standard allows that).  
 The problem is with the implementation of __ExceptionPtr::_CallCopyCtor in crt/src/stl/excptptr.cpp .  It essentially does a test that goes like "Is there a copy constructor? No? Great, we can do a memcpy then!".  
 Another problem is that the test ignores access specifiers.  If you provide a copy constructor but make it private , it will be called.  
 The test is done at runtime, so no chance for a compile-time error, and, unfortunately, that's the only way to do it: there is no general compile-time test that will tell what type of exception object an std::exception_ptr will point to in all cases.  
The proposed resolution for Core issue 1863 aims to avoid this problem by requiring that
[...] the constructor selected for a copy-initialization considering the thrown object as an lvalue shall be non-deleted and accessible. [...]
 The issue is in Ready status, which is one step away from adoption.  
So, while this is definitely a problem with MSVC, it's also related to an open issue in the Standard. For now, it looks like a good idea to provide a copy constructor for exception objects, even if the Standard doesn't require it (yet).
Update: The resolution for issue 1863 has been adopted into the N4567 working draft, so compilers implementing the change are required to reject the code if a suitable constructor is not available.
It seems that MSVC 2015 still calls the copy constructor, even though it's marked deleted. To get around this issue, I defined the copy constructor.
The issue with the printout was because there was no printing in the copy constructor. I added some, and the constructor/destructor count matched.
Still, MSVC 2015 shouldn't be calling the copy-constructor if it's marked deleted. If it must be called, then it should issue an error.
链接地址: http://www.djcxy.com/p/30732.html上一篇: std :: async临时将来返回
