std::future 在调用 get() 后仍然有效(抛出异常)
std::future still valid after calling get() (which throws an exception)
根据 cppreference,调用 std::future::get
后:
valid() is false after a call to this method.
此外,来自 cplusplus.com:
Once the shared state is ready, the function unblocks and returns (or throws) releasing its shared state. This makes the future object no longer valid: this member function shall be called once at most for every future shared state.
并且在异常安全下:
The function throws the exception stored in the shared state when the provider makes it ready by setting it to an exception. Note that in this case a basic guarantee is offered, with the future object being modified to no longer be a valid future (which is itself a valid state for object of this type, despite its name).
两个描述都没有区分对 get
的调用,returns 一个值和一个抛出关于未来对象失效的异常的值。
但是,所描述的行为并不是我在这个示例代码中看到的:
#include <chrono>
#include <future>
#include <iostream>
#include <stdexcept>
int foo()
{
throw std::runtime_error("foo exception");
}
int main()
{
std::future<int> futInt;
futInt = std::async(std::launch::async, []() { return foo(); });
while( !(futInt.valid() && futInt.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) )
;
if( futInt.valid() )
{
int val;
try
{
val = futInt.get();
}
catch( const std::exception& e )
{
std::cout << e.what() << std::endl;
}
}
if( futInt.valid() )
{
std::cout << "This is TOTALLY UNEXPECTED!!!" << std::endl;
}
else
{
std::cout << "This is expected." << std::endl;
}
return 0;
}
我看到的输出是:
foo exception
This is TOTALLY UNEXPECTED!!!
我正在使用 Visual Studio Premium 2013,版本 12.0.30501.00 Update 2。这是编译器的错误,还是在出现异常时的正确行为?我一直无法找到与此相关的任何错误报告,因此不确定这是否是预期的行为。
编辑 - <未来> 实施调查
深入研究 std::future
实现,_Associated_state
对象被标记为 _Retrieved = true;
AFTER 检查并抛出相关异常(如果任何):
virtual _Ty& _Get_value(bool _Get_only_once)
{ // return the stored result or throw stored exception
unique_lock<mutex> _Lock(_Mtx);
if (_Get_only_once && _Retrieved)
_Throw_future_error(
make_error_code(future_errc::future_already_retrieved));
if (_Exception)
_Rethrow_future_exception(_Exception);
_Retrieved = true;
_Maybe_run_deferred_function(_Lock);
while (!_Ready)
_Cond.wait(_Lock);
if (_Exception)
_Rethrow_future_exception(_Exception);
return (_Result);
}
我的猜测是异常检查和 _Retrieved = true;
应该交换 - 对象应该立即设置为检索(在 _Get_only_once
检查之后),然后所有其他逻辑应该遵循。因此,编译器错误。
编辑 - 解决方法
我认为以下应该足以代替直接调用 get
直到实施修复:
template<typename T>
T getFromFuture(std::future<T>& fut)
{
try
{
return fut.get();
}
catch( ... )
{
fut = {};
throw;
}
}
我在 Linux 上用 gcc 5.2.0 和 clang 3.7.0 编译——两次都是 64 位。
运行 程序总是导致
foo exception
This is expected.
在我看来,Visual 2013 对此处理不当。
另见:
C++ §30.6.6/16-17
Throws: the stored exception, if an exception was stored in the shared state.
Postcondition: valid() == false
.
后置条件在抛出之后提到,因此必须始终成立,即使抛出异常也是如此。至少这是我的解释,虽然我不会说标准语。
我猜你也应该尝试使用 Visual Studio 2015 并报告一个错误,如果它显示相同的处理。
根据 cppreference,调用 std::future::get
后:
valid() is false after a call to this method.
此外,来自 cplusplus.com:
Once the shared state is ready, the function unblocks and returns (or throws) releasing its shared state. This makes the future object no longer valid: this member function shall be called once at most for every future shared state.
并且在异常安全下:
The function throws the exception stored in the shared state when the provider makes it ready by setting it to an exception. Note that in this case a basic guarantee is offered, with the future object being modified to no longer be a valid future (which is itself a valid state for object of this type, despite its name).
两个描述都没有区分对 get
的调用,returns 一个值和一个抛出关于未来对象失效的异常的值。
但是,所描述的行为并不是我在这个示例代码中看到的:
#include <chrono>
#include <future>
#include <iostream>
#include <stdexcept>
int foo()
{
throw std::runtime_error("foo exception");
}
int main()
{
std::future<int> futInt;
futInt = std::async(std::launch::async, []() { return foo(); });
while( !(futInt.valid() && futInt.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) )
;
if( futInt.valid() )
{
int val;
try
{
val = futInt.get();
}
catch( const std::exception& e )
{
std::cout << e.what() << std::endl;
}
}
if( futInt.valid() )
{
std::cout << "This is TOTALLY UNEXPECTED!!!" << std::endl;
}
else
{
std::cout << "This is expected." << std::endl;
}
return 0;
}
我看到的输出是:
foo exception
This is TOTALLY UNEXPECTED!!!
我正在使用 Visual Studio Premium 2013,版本 12.0.30501.00 Update 2。这是编译器的错误,还是在出现异常时的正确行为?我一直无法找到与此相关的任何错误报告,因此不确定这是否是预期的行为。
编辑 - <未来> 实施调查
深入研究 std::future
实现,_Associated_state
对象被标记为 _Retrieved = true;
AFTER 检查并抛出相关异常(如果任何):
virtual _Ty& _Get_value(bool _Get_only_once)
{ // return the stored result or throw stored exception
unique_lock<mutex> _Lock(_Mtx);
if (_Get_only_once && _Retrieved)
_Throw_future_error(
make_error_code(future_errc::future_already_retrieved));
if (_Exception)
_Rethrow_future_exception(_Exception);
_Retrieved = true;
_Maybe_run_deferred_function(_Lock);
while (!_Ready)
_Cond.wait(_Lock);
if (_Exception)
_Rethrow_future_exception(_Exception);
return (_Result);
}
我的猜测是异常检查和 _Retrieved = true;
应该交换 - 对象应该立即设置为检索(在 _Get_only_once
检查之后),然后所有其他逻辑应该遵循。因此,编译器错误。
编辑 - 解决方法
我认为以下应该足以代替直接调用 get
直到实施修复:
template<typename T>
T getFromFuture(std::future<T>& fut)
{
try
{
return fut.get();
}
catch( ... )
{
fut = {};
throw;
}
}
我在 Linux 上用 gcc 5.2.0 和 clang 3.7.0 编译——两次都是 64 位。 运行 程序总是导致
foo exception
This is expected.
在我看来,Visual 2013 对此处理不当。 另见:
C++ §30.6.6/16-17
Throws: the stored exception, if an exception was stored in the shared state.
Postcondition:
valid() == false
.
后置条件在抛出之后提到,因此必须始终成立,即使抛出异常也是如此。至少这是我的解释,虽然我不会说标准语。
我猜你也应该尝试使用 Visual Studio 2015 并报告一个错误,如果它显示相同的处理。