A future
is basically a wrapper around some shared state. valid
is telling you whether the future
actually has some shared state or not.
A future
will have no shared state if it was default constructed, or if its shared state has already been retrieved, or if it was the source of a move, so some other future
now owns the shared state this one used to.
At least as I understand what you're writing, chances are pretty good that you really want to just return the future
as is, and have no reason to check whether it's valid before you return it--in particular, if you receive a future
that's not valid, the equivalent of a "null future" that you'd return to signal that would be a default-constructed future
that would signal "not present" ...by its valid
returning false
. In other words, you'd signal the lack of a future
by returning exactly the same sort of one as you received in the first place.
I suppose you could separate out the signaling of "not present" from the future
if you really wanted to. For example, you could define your function something like this:
std::optional<std::future<ApiClass>> myMethodReturningFuture() {
In an ideal1 world with every class fully decomposed and having only a single responsibility, std::future
probably wouldn't exist--you'd instead have something like std::optional<std::shared_state<T>>
in its place, and the optional
part would be responsible for saying whether it's present or not, and the shared_state
would only deal with being shared state. But that's not the world we live in, so future
's valid
is basically acting like the optional
, and telling you whether the shared state is present or not.
If you were going to do this, you'd need to account for the fact that a future
is moveable but not copyable, so when you assign one to a variable, you need to use std::move
to get it back out of that variable. For example, here's some trivial (but complete) code to do the check and return an 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. Well, ideal from one viewpoint. Frankly, I'm not sure this design would turn out so ideal in real use. Then again, I'm not sure `future` is entirely ideal in real use either.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…