ISO/IEC JTC1 SC22 WG21
Document Number: P0241R0
Audience: Library Evolution Working Group
Matt Calabrese (metaprogrammingtheworld@
2016-02-11
This paper uses the updated void
type described in P0146R11 to simplify the specification of std::promise
, std::future
, and std::shared_future
by removing the specializations for void
. In order to ease transition of user code, some backwards compatibility is provided by way of deprecated functions that share an interface with those of the specializations of the current standard.
In C++14 and prior C++ standards, void
is a very unique type in the language and requires special handling in generic code. Perhaps the most prominent example of this is in the standard library regarding the void
specializations for std::promise
, std::future
, and std::shared_future
. The requirement of these specializations complicates standard library implementations, usage of std::promise
and std::future
in generic code, and even complicates the standard itself. With a void
type that is an object type, as proposed in P0146R11, the need for these specializations goes away, and it would be beneficial both for the standard and for developers to remove these specializations.
With the proposed void
type, the simplest change to std::promise
and std::future
would be to simply remove the explicit specializations for void
. This would work without doing anything else, however, there would be considerable breakage of existing code.
std::promise
Perhaps the most obvious breaking change that comes from the removal of the void
specializations is that the current function signature of the set_value
member function of std::promise<void>
differs from that of the generic template definition. More precisely, in the void
specialization, set_value
has no parameters (other than this
) while in the generic definition, set_value
is overloaded to take either an lvalue-reference-to-const or an rvalue-reference to the promise's value type. The implication of this is that users who dealt with an instance of std::promise<void>
and invoked set_value
in existing code would have their code now fail to compile. While this might be considered acceptable, it is suggested here that we can ease the transition by introducing a deprecated overload to the generic definition of std::promise
that is only callable for void
and that shares an interface with the existing set_value
for void
. The suggested overload to be added is as follows:
[[deprecated("Prefer invoking as set_value({}).")]] void set_value() { // Definition is only provided for example. set_value({}); }
In order to provide the deprecated set_value
function for std::promise<void>
, it may be defined in a CRTP base type of the promise
. In addition to the above change, a similar update is suggested regarding set_value_at_thread_exit
.
Somewhat more implicit in the formal changes detailed below is the fact that, because void
types are now handled via the generic template definition, cv-void types are directly usable with std::promise
and std::future
. In existing C++, such specializations would be ill-formed because the standard only provides explicit specializations for the unqualified void
type. Similarly, reference-to-void types are also implicitly supported with the proposed void
type, as the reference specializations already provided by the standard will work without modification for reference-to-void types.
Remove void
specialization of std::promise
from §30.6.1 [futures.overview] synopsis in paragraph 1:
template <> class promise<void>;
Remove void
specialization of std::future
from §30.6.1 [futures.overview] synopsis in paragraph 1:
template <> class future<void>;
Remove void
specialization of std::shared_future
from §30.6.1 [futures.overview] synopsis in paragraph 1:
template <> class shared_future<void>;
Modify §30.6.5 [futures.promise] paragraph 1:
The implementation shall provide the template promise andtwothe specializations,promise<R&>and promise< void>.TheseThis specialization differs only in the argument type of the member functions set_value,and set_value_at_thread_exit, as set out initsthe descriptions, below.
Modify §30.6.5 [futures.promise] declarations and paragraph 15:
void promise::set_value(const R& r); void promise::set_value(R&& r); void promise<R&>::set_value(R& r); void promise<void>::set_value(); 15 Effects: atomically stores the value r in the shared state and makes that state ready (30.6.4). The fourth version is only present when the template argument of promise is void. It is functionally equivalent to calling the second version with a void argument. [ Note: The fourth version is considered deprecated. Users should prefer to use either the first version or the second version instead. — end note ]
Modify §30.6.5 [futures.promise] declarations and paragraph 21:
void promise::set_value_at_thread_exit(const R& r); void promise::set_value_at_thread_ exit(R&& r); void promise<R&>::set_value_at_ thread_exit(R& r); void promise<void>::set_value_at_ thread_exit(); 21 Effects: Stores the value r in the shared state without making that state ready immediately. Schedules that state to be made ready when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed. The fourth version is only present when the template argument of promise is void. It is functionally equivalent to calling the second version with a void argument. [ Note: The fourth version is considered deprecated. Users should prefer to use either the first version or the second version instead. — end note ]
Modify §30.6.6 [futures.unique_future] paragraph 4:
The implementation shall provide the template future andtwothe specializations,future<R&>and future< void>.TheseThis specialization differs only in the return type and return value of the member function get, as set out in its description, below.
Modify §30.6.6 [futures.unique_future] declarations and paragraphs 14 through 16:
R future::get(); R& future<R&>::get();void future<void>::get();14 Note: as described above, the template and itstworequired specializationsdiffers only in the return type and return value of the member function get. 15 Effects: wait()s until the shared state is ready, then retrieves the value stored in the shared state. 16 Returns: — future::get() returns the value v stored in the object’s shared state as std::move(v). — future<R&>::get() returns the reference stored as value in the object’s shared state.— future<void>::get() returns nothing.
Modify §30.6.7 [futures.shared_future] paragraph 4:
The implementation shall provide the template shared_future andtwothe specializations,shared_future<R&>and shared_future<void>.TheseThis specialization differs only in the return type and return value of the member function get, as set out in its description, below.
Modify §30.6.7 [futures.shared_future] declarations and paragraphs 16 through 19:
const R& shared_future::get() const; R& shared_future<R&>::get() const;void shared_future<void>::get() const;16 Note: as described above, the template and itstworequired specializationsdiffers only in the return type and return value of the member function get. 17 Note: access to a value object stored in the shared state is unsynchronized, so programmers should apply only those operations on R that do not introduce a data race (1.10). 18 Effects: wait()s until the shared state is ready, then retrieves the value stored in the shared state. 19 Returns: — shared_future::get() returns a const reference to the value stored in the object’s shared state. [ Note: Access through that reference after the shared state has been destroyed produces undefined behavior; this can be avoided by not storing the reference in any storage with a greater lifetime than the shared_future object that returned the reference. — end note ] — shared_future<R&>::get() returns the reference stored as value in the object’s shared state.— shared_future<void>::get() returns nothing.
[1] Matt Calabrese: "Regular Void" http://www.open-std.org/jtc1/