C++ 未来处理

C++ Future handling

我正在构建一个 C++ 程序,它使用 API 来尝试获取一些关于包裹跟踪信息的数据,其中一些方法是 returning 未来,如果未来已经实现,我想return它到主程序。考虑以下因素:

std::future<ApiClass> myMethodReturningFuture() {
    /* some processing */
    std::future<ApiClass> future = api.call();
    return future;
}

我的问题是如何正确处理未来(例如,检查它是否包含正确的信息,调用 future.valid() 似乎总是 return 1)。另外,如果未来不是完全有效的,如何从这个函数 return NULL 或 nullptr ?让我们考虑一下这种情况:

std::future<ApiClass> myMethodReturningFuture() {
    /* some processing */
    try {
        std::future<ApiClass> future = api.call();
        return future;
    } catch (std::exception& e) {
        return nullptr // or NULL;
    }
}

上面的代码片段无法编译。即使我将函数 return 设为指针,我也会收到有关“使用已删除函数 std::future”的错误消息。像这样:

std::future<ApiClass>* myMethodReturningFuture() {
    /* some processing */
    try {
        std::future<ApiClass>* future = &api.call();
        return future;
    } catch (std::exception& e) {
        return nullptr // or NULL;
    }
}

A future 基本上是一些共享状态的包装器。 valid 告诉你 future 是否真的有一些共享状态。

一个 future 如果它是默认构造的,或者如果它的共享状态已经被检索,或者如果它是移动的来源,那么 future 将没有共享状态,所以其他一些 future现在拥有这个曾经拥有的共享状态。

至少据我了解你在写什么,很有可能你真的只想 return future 原样,并且没有理由检查它之前是否有效你 return 它——特别是,如果你收到一个无效的 future,相当于你 return 表示默认的“无效未来”-构建了 future 信号“不存在”...通过其 valid returning false。换句话说,您可以通过 return 发送与您最初收到的完全相同的类型来表示缺少 future

我想你 可以 将“不存在”的信号与 future 分开,如果你真的想的话。例如,您可以这样定义您的函数:

std::optional<std::future<ApiClass>> myMethodReturningFuture() {

在一个理想的1 世界中,每个 class 都完全分解并且只有一个责任,std::future 可能不会存在--你' d 取而代之的是 std::optional<std::shared_state<T>> 之类的东西,而 optional 部分将负责说明它是否存在,而 shared_state 处理共享状态。但这不是我们生活的世界,所以 futurevalid 基本上就像 optional 一样,告诉你共享状态是否存在。

如果你打算这样做,你需要考虑到 future 是可移动但不可复制的事实,所以当你将一个分配给一个变量时,你需要使用 std::move 将其从该变量中取回。例如,这里有一些简单(但完整)的代码来进行检查和 return 一个 std::optional<std::future<T>>:

#include <optional>
#include <future>

struct ApiClass {
    int i;
};

struct Api { 
    std::future<ApiClass> call() const {
        // create and return a (trivial) future.
        return std::async(std::launch::async, []{ return ApiClass {1}; });
    }
};

std::optional<std::future<ApiClass>> myMethodReturningFuture() {
    Api api;
    // here we create a `future` object, so it's an lvalue, not an rvalue
    std::future<ApiClass> future {api.call()};
    // so when we want to create our optional<future>, we need to tell the
    // compiler to move instead of copying the object we created:
    return future.valid() == 1 ? std::make_optional(std::move(future)) : std::nullopt;
}

1. 嗯,从一个角度来看是理想的。坦率地说,我不确定这种设计在实际使用中是否会如此理想。话又说回来,我不确定“future”在实际使用中是否完全理想。