调用get()后抛出异常,std :: future仍然有效

Alp*_*ion 9 c++ stl future exception c++11

根据cppreference,在致电std::future::get:

调用此方法后,valid()为false.

另外,来自cplusplus.com:

共享状态准备就绪后,该函数将解除阻塞并返回(或抛出)释放其共享状态.这使得将来的对象不再有效:对于每个未来的共享状态,该成员函数最多应被调用一次.

并在异常安全下:

当提供程序通过将其设置为异常使其准备就绪时,该函数会抛出存储在共享状态中的异常.请注意,在这种情况下,会提供基本保证,将来的对象修改为不再是有效的未来(对于此类型的对象,它本身就是一个有效的状态,尽管它的名称).

这两个描述都没有区分get返回值的调用与抛出有关未来对象失效的异常的调用.

但是,所描述的行为不是我在这个示例代码中看到的:

#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;
}
Run Code Online (Sandbox Code Playgroud)

我看到的输出是:

foo exception
This is TOTALLY UNEXPECTED!!!
Run Code Online (Sandbox Code Playgroud)

我正在使用Visual Studio Premium 2013,版本12.0.30501.00更新2.这是编译器的错误,或者事实上,在异常情况下是否正确?我一直无法找到有关此问题的任何错误报告,所以不确定这是否是预期的行为.

编辑 - <future>实施调查

std::future稍微深入了解实现,在检查并抛出相关异常(如果有)_Associated_state标记对象:_Retrieved = true;

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);
    }
Run Code Online (Sandbox Code Playgroud)

我的猜测是异常检查并且_Retrieved = true;应该交换 - 对象应该立即设置为检索(_Get_only_once检查后),然后应该遵循所有其他逻辑.Ergo,编译错误.

编辑 - 解决方法

我认为以下内容应该足以代替直接调用,get直到实现修复:

template<typename T>
T getFromFuture(std::future<T>& fut)
{
    try
    {
        return fut.get();
    }
    catch( ... )
    {
        fut = {};
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)

mfu*_*chs 3

我在 Linux 上使用 gcc 5.2.0 和 clang 3.7.0 进行编译——两次都是 64 位。\n运行该程序总是会导致

\n
foo exception\nThis is expected.\n
Run Code Online (Sandbox Code Playgroud)\n

在我看来,Visual 2013 处理此问题的方式不正确。\n另请参阅:

\n

C++ \xc2\xa730.6.6/16-17

\n
\n

抛出:如果异常存储在共享状态中,则存储异常。

\n

后置条件: valid() == false .

\n
\n

后置条件是在抛出之后提到的,因此即使抛出异常,也必须始终保持。至少这是我的解释,尽管我不会说标准语。

\n

我想您可能还应该尝试使用 Visual Studio 2015,如果显示相同的处理,则报告错误。

\n