Document number:  N4313
Revises:          N4123
Date:             2014-11-21
Project:          Programming Language C++
Reference:        ISO/IEC IS 14882:2011(E)
Reply to:         Artur Laksberg, Microsoft Corp. arturl@microsoft.com






Improvements to the Concurrency Technical Specification, revision 1

1

Introduction

[introduction]
1.1

Motivation and Goals

[motivation]

This document proposes improvements to N4107, the current Working Draft of the Concurrency TS.

This document is a revision of N4123, which was presented to SG1 and LEWG at the Urbana meeting.

This document is motivated by N4032 and N4048, which were presented to SG1 at the Rapperswil meeting. This document collects the parts of these papers that received positive feedback from SG1 and presents them as a combined set of proposed changes to the Concurrency TS.

The paper also addresses numerous issues discovered and reported to the author by various individuals, namely Agustín Bergé, Vicente J. Botet Escriba, Hartmut Kaiser and Anthony Williams.

In addition to the changes proposed in N4123, this document implements the following suggestions from SG1 and LEWG received at the Urbana meeting:

  • Do not introduce std::experimental::async function. This avoids having to specify execution policies. This also avoids the question of whether a future created by async should block in the destructor.
  • Remove when_any_back, and change the return type of when_any to return the sequence of futures as well as the index of the ready future.

Finally, the paper implements a small fix to the signature of make_ready_future to support reference_wrapper types. This fix was requested during the Urbana meeting but was not reviewed by SG1 or LEWG.

The author seeks feedback from LWG on this document. If approved by LWG, the changes proposed in this document will be incorporated into the next Working Paper of the Concurrency TS.

1.2

How To Read This Document

[how-to-read]

The proposed changes are presented as "diffs" to N4107 marked as insertions and deletions.

2

General

[general]
2.1

Namespaces, headers, and modifications to standard classes

[general.namespaces]

Some of the extensions described in this Technical Specification represent types and functions that are currently not part of the C++ Standards Library, and because these extensions are experimental, they should not be declared directly within namespace std. Instead, such extensions are declared in namespace std::experimental.

[ Note: Once standardized, these components are expected to be promoted to namespace std. end note ]

Unless otherwise specified, references to such entities described in this Technical Specification are assumed to be qualified with std::experimental, and references to entities described in the C++ Standard Library are assumed to be qualified with std::.

Since the extensions described in this technical specification are experimental and not part of the C++ standard library, they should not be declared directly within namespace std. Unless otherwise specified, all components described in this technical specification either:

  • modify an existing interface in the C++ Standard Library in-place,
  • are declared in a namespace whose name appends ::experimental::concurrency_v1 to a namespace defined in the C++ Standard Library, such as std, or
  • are declared in a subnamespace of a namespace described in the previous bullet, whose name is not the same as an existing subnamespace of namespace std.

Each header described in this technical specification shall import the contents of std::experimental::concurrency_v1 into std::experimental as if by

namespace std {
  namespace experimental {
    inline namespace concurrency_v1 {}
  }
}

Unless otherwise specified, references to other entities described in this technical specification are assumed to be qualified with std::experimental::concurrency_v1::, and references to entities described in the standard are assumed to be qualified with std::.

Extensions that are expected to eventually be added to an existing header <meow> are provided inside the <experimental/meow> header, which shall include the standard contents of <meow> as if by

#include <meow>

New headers are also provided in the <experimental/> directory, but without such an #include.

Table 1 — C++ library headers
  • <experimental/future>
2.2

Future plans (Informative)

[general.plans]

This section describes tentative plans for future versions of this technical specification and plans for moving content into future versions of the C++ Standard.

The C++ committee intends to release a new version of this technical specification approximately every year, containing the library extensions we hope to add to a near-future version of the C++ Standard. Future versions will define their contents in std::experimental::concurrency_v2, std::experimental::concurrency_v3, etc., with the most recent implemented version inlined into std::experimental.

When an extension defined in this or a future version of this technical specification represents enough existing practice, it will be moved into the next version of the C++ Standard by removing the experimental::concurrency_vN segment of its namespace and by removing the experimental/ prefix from its header's path.

2.3

Feature-testing recommendations (Informative)

[general.feature.test]

For the sake of improved portability between partial implementations of various C++ standards, WG21 (the ISO technical committee for the C++ programming language) recommends that implementers and programmers follow the guidelines in this section concerning feature-test macros. [ Note: WG21's SD-6 makes similar recommendations for the C++ Standard itself. end note ]

Implementers who provide a new standard feature should define a macro with the recommended name, in the same circumstances under which the feature is available (for example, taking into account relevant command-line options), to indicate the presence of support for that feature. Implementers should define that macro with the value specified in the most recent version of this technical specification that they have implemented. The recommended macro name is "__cpp_lib_experimental_" followed by the string in the "Macro Name Suffix" column.

Programmers who wish to determine whether a feature is available in an implementation should base that determination on the presence of the header (determined with __has_include(<header/name>)) and the state of the macro with the recommended name. (The absence of a tested feature may result in a program with decreased functionality, or the relevant functionality may be provided in a different way. A program that strictly depends on support for a feature can just try to use the feature unconditionally; presumably, on an implementation lacking necessary support, translation will fail.)

Table 2 — Significant features in this technical specification
Doc. No. Title Primary Section Macro Name Suffix Value Header
N3875 Improvements to std::future and Related APIs 3 future_continuations 201410 <experimental/future>
3

Improvements to std::future<T> and Related APIs

[futures]
3.1

General

[futures.general]

The extensions proposed here are an evolution of the functionality of std::future and std::shared_future. The extensions enable wait free composition of asynchronous operations. Class templates std::promise and std::packaged_task are also updated to be compatible with the updated std::future.

3.2

Header <experimental/future> synopsis

[header.future.synop]
#include <future>

namespace std {
  namespace experimental {
  inline namespace concurrency_v1 {

    template <class R> class promise;
    template <class R> class promise<R&>;
    template <> class promise<void>;

    template <class R>
      void swap(promise<R>& x, promise<R>& y) noexcept;

    template <class R> class future;
    template <class R> class future<R&>;
    template <> class future<void>;
    template <class R> class shared_future;
    template <class R> class shared_future<R&>;
    template <> class shared_future<void>;

    template <class> class packaged_task; // undefined
    template <class R, class... ArgTypes>
      class packaged_task<R(ArgTypes...)>;

    template <class R, class... ArgTypes>
      void swap(packaged_task<R(ArgTypes...)>&, packaged_task<R(ArgTypes...)>&) noexcept;

    template <class T>
      future<decay_t<T>> make_ready_future(T&& value);
    future<void> make_ready_future();

    future<T> make_exceptional_future(exception_ptr ex);
    template <class T, class E>
      future<T> make_exceptional_future(E ex);

    template <class InputIterator>
      see below when_all(InputIterator first, InputIterator last);
    template <class... Futures>
      see below when_all(Futures&&... futures);

    template <class Sequence>
      struct when_any_result;

    template <class InputIterator>
      see below when_any(InputIterator first, InputIterator last);
    template <class... Futures>
      see below when_any(Futures&&... futures);

  } // namespace concurrency_v1
  } // namespace experimental

  template <class R, class Alloc>
    struct uses_allocator<experimental::promise<R>, Alloc>;

  template <class R, class Alloc>
    struct uses_allocator<experimental::packaged_task<R>, Alloc>;

} // namespace std
3.3

Changes to cClass template future

[futures.unique_future]

The specification of all declarations within this sub-clause 3.3 and its sub-clauses are the same as the corresponding declarations, as specified in C++14 §30.6.6, unless explicitly specified otherwise.

To the class declaration found in C++14 §30.6.6 paragraph 3, add the following to the public functions:

bool is_ready() const;

future(future<future<R>>&&) noexcept;

template <typename F>
see below then(F&& func);

template <typename F>
see below then(launch policy, F&& func);

namespace std {
  namespace experimental {
  inline namespace concurrency_v1 {

    template <class R>
    class future {
    public:
      future() noexcept;
      future(future&&) noexcept;
      future(const future&) = delete;
      future(future<future<R>>&&) noexcept;
      ~future();
      future& operator=(const future&) = delete;
      future& operator=(future&&) noexcept;
      shared_future<R&> share();

      // retrieving the value
      see below get();

      // functions to check state
      bool valid() const noexcept;
      bool is_ready() const noexcept;

      void wait() const;
      template <class Rep, class Period>
        future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
      template <class Clock, class Duration>
        future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;

      // continuations
      template <class F>
        see below then(F&& func);
    };

  } // namespace concurrency_v1
  } // namespace experimental
  } // namespace std

In C++14 §30.6.6 between paragraphs 9 and 10, add the following:

future(future<future<R>>&& rhs) noexcept;
Effects:
Constructs a future object from the shared state referred to by rhs and unwrapping the inner future. The future becomes ready when one of the following occurs:
  • Both the outer and the inner futures are ready. The future stores the value or the exception from the inner future.
  • The outer future is ready but the inner future is invalid. The future stores an exception of type std::future_error, with an error condition of std::future_errc::broken_promise.
Postconditions:
  • valid() returns the same value as rhs.valid() prior to the constructor invocation.
  • valid() == true.
  • rhs.valid() == false.

After C++14 §30.6.6 paragraph 26, add the following:

The member function template then provides a mechanism for attaching a continuation to a future object, which will be executed as specified below.


template <typenameclass F>
see below then(F&& func);
template <typename F>
see below then(launch policy, F&& func);

Requires:
INVOKE(DECAY_COPY (std::forward<F>(func)), std::move(*this)) shall be a valid expression.
Notes:
The two functions differ only by input parameters. The first only takes a callable object which accepts a future object as a parameter. The second function takes a launch policy as the first parameter and a callable object as the second parameter. The function takes a callable object which accepts a future object as a parameter.
Effects:
The function creates a shared state that is associated with the returned future object. The further behavior of the function is as follows.
  • The continuation INVOKE(DECAY_COPY (std::forward<F>(func))) is called when the object's shared state is ready (has a value or exception stored).
  • When the object's shared state is ready, the continuation INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this)) is called on an unspecified thread of execution with the call to DECAY_COPY() being evaluated in the thread that called then.
  • The continuation launches according to the specified launch policy.
  • When the launch policy is not provided the continuation inherits the parent's launch policy.
  • Any value returned from the continuation is stored as the result in the shared state of the resulting future. Any exception propagated from the execution of the continuation is stored as the exceptional result in the shared state of the resulting future.
  • If the parent was created with std::promise or with a packaged_task (has no associated launch policy), the continuation behaves the same as in the second overload with a policy argument of launch::async | launch::deferred and the same argument for func.
  • If the parent has a policy of launch::deferred, then it is filled by calling wait() or get() on the resulting future. [ Example:
    
    auto f1 = async(launch::deferred, [] { return 1; });
    
    auto f2 = f1.then([](future n) { return 2; });
    
    f2.wait(); // execution of f1 starts here, followed by f2
          
    end example ]
Returns:
The return type of then depends on the return type of the closure func as defined below:
  • When result_of_t<decay_t<F>(future<R>)> is future<R2>, the function returns future<R2>.
  • Otherwise, the function returns future<result_of_t<decay_t<F>(future<R>)>>.

    [ Note: The first rule above is called implicit unwrapping. Without this rule, the return type of then taking a closure returning a future<R> would have been future<future<R>>. This rule avoids such nested future objects. The type of f2 below is future<int> and not future<future<int>>: [ Example:
    future<int> f1 = g();
    future<int> f2 = f1.then([](future<int> f) {
                        future<int> f3 = h();
                        return f3;
                     });
    
    end example ]
    end note ]
Postconditions:
  • The future object is moved to the parameter of the continuation function.
  • valid() == false on the original future.
  • valid() == true on the future returned from then. object immediately after it returns.
    Notes:
    In case of implicit unwrapping, the validity of the future returned from then cannot be established until after the completion of the continuation. If it is not valid, the resulting future becomes ready with an exception of type std::future_error, with an error condition of std::future_errc::broken_promise.
bool is_ready() const noexcept;
Returns:
true if the shared state is ready, otherwise false.
3.4

Changes to cClass template shared_future

[futures.shared_future]

The specification of all declarations within this sub-clause 3.4 and its sub-clauses are the same as the corresponding declarations, as specified in C++14 §30.6.7, unless explicitly specified otherwise.

To the class declaration found in C++14 §30.6.7 paragraph 3, add the following to the public functions:


bool is_ready() const;

template <typename F>
see below then(F&& func);

template <typename F>
see below then(launch policy, F&& func);

namespace std {
  namespace experimental {
  inline namespace concurrency_v1 {

    template <class R>
    class shared_future {
    public:
      shared_future() noexcept;
      shared_future(const shared_future&) noexcept;
      shared_future(future<R>&&) noexcept;
      shared_future(shared_future&&) noexcept;
      shared_future(future<shared_future<R>>&& rhs) noexcept;
      ~shared_future();
      shared_future& operator=(const shared_future&);
      shared_future& operator=(shared_future&&) noexcept;

      // retrieving the value
      see below get();

      // functions to check state
      bool valid() const noexcept;
      bool is_ready() const noexcept;

      void wait() const;
      template <class Rep, class Period>
        future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
      template <class Clock, class Duration>
        future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;

      // continuations
      template <class F>
        see below then(F&& func) const;
    };

  } // namespace concurrency_v1
  } // namespace experimental
  } // namespace std

After C++14 §30.6.7 paragraph 10, add the following:

shared_future(future<shared_future<R>>&& rhs) noexcept;
Effects:
Constructs a shared_future object from the shared state referred to by rhs. The shared_future becomes ready when one of the following occurs:
  • Both the outer future and the inner shared_future are ready. The shared_future stores the value or the exception from the inner shared_future.
  • The outer future is ready but the inner shared_future is invalid. The shared_future stores an exception of type std::future_error, with an error condition of std::future_errc::broken_promise.
Postconditions:
  • valid() returns the same value as rhs.valid() prior to the constructor invocation.
  • valid() == true.
  • rhs.valid() == false.

After C++14 §30.6.7 paragraph 28, add the following:

The member function template then provides a mechanism for attaching a continuation to a shared_future object, which will be executed as specified below.


template <typenameclass F>
see below then(F&& func) const;

template <class F>
see below then(launch policy, F&& func) const;
Requires:
INVOKE(DECAY_COPY (std::forward<F>(func)), *this) shall be a valid expression.
Notes:
The two functions differ only by input parameters. The first only takes a callable object which accepts a shared_future object as a parameter. The second function takes a launch policy as the first parameter and a callable object as the second parameter. The function takes a callable object which accepts a shared_future object as a parameter.
Effects:
The function creates a shared state that is associated with the returned future object. The further behavior of the function is as follows.
  • The continuation INVOKE(DECAY_COPY (std::forward<F>(func)), *this) is called when the object's shared state is ready (has a value or exception stored).
  • When the object's shared state is ready, the continuation INVOKE(DECAY_COPY(std::forward<F>(func)), *this) is called on an unspecified thread of execution with the call to DECAY_COPY() being evaluated in the thread that called then.
  • The continuation launches according to the specified policy.
  • When the launch policy is not provided the continuation inherits the parent's launch policy.
  • Any value returned from the continuation is stored as the result in the shared state of the resulting future. Any exception propagated from the execution of the continuation is stored as the exceptional result in the shared state of the resulting future.
  • If the parent was created with std::promise (has no associated launch policy), the continuation behaves the same as in the second function with a policy argument of launch::async | launch::deferred and the same argument for func.
  • If the parent has a policy of launch::deferred, then it is filled by calling wait() or get() on the resulting shared_future. [ Note: This is similar to future. See example in 3.3. end note ]
Returns:
The return type of then depends on the return type of the closure func as defined below:
  • When result_of_t<decay_t<F>(shared_future<R>)> is future<R2>, the function returns future<R2>.
  • Otherwise, the function returns future<result_of_t<decay_t<F>(shared_future<R>)>>.

    [ Note: This is analogous to future. See the notes on future::then return type in 3.3. end note ]

Postconditions:
  • The shared_future passed to the continuation function is a copy of the original shared_future.
  • valid() == true on the original shared_future object. valid() == true on the shared_future returned from then.
    Notes:
    In case of implicit unwrapping, the validity of the future returned from then cannot be established until after the completion of the continuation. In such case, the resulting future becomes ready with an exception of type std::future_error, with an error condition of std::future_errc::broken_promise.
bool is_ready() const noexcept;
Returns:
true if the shared state is ready, otherwise false.
3.5

Class template promise

[futures.promise]

The specification of all declarations within this sub-clause 3.5 and its sub-clauses are the same as the corresponding declarations, as specified in C++14 §30.6.5, unless explicitly specified otherwise.

The future returned by the function get_future is the one defined in the experimental namespace (3.3).

3.6

Class template packaged_task

[futures.task]

The specification of all declarations within this sub-clause 3.6 and its sub-clauses are the same as the corresponding declarations, as specified in C++14 §30.6.9, unless explicitly specified otherwise.

The future returned by the function get_future is the one defined in the experimental namespace (3.3).

3.7

Function template when_all

[futures.when_all]

A new section 30.6.10 shall be inserted at the end of C++14 §30.6. Below is the content of that section.

The function template when_all creates a future object that becomes ready when all elements in a set of future and shared_future objects become ready.


template <class InputIterator>
see below
future<vector<typename iterator_traits<InputIterator>::value_type>>
when_all(InputIterator first, InputIterator last);

template <typenameclass... TFutures>
see below
future<tuple<decay_t<Futures>...>> when_all(TFutures&&... futures);
Requires:
  • For the first overload, iterator_traits<InputIterator>::value_type must be future<R> or shared_future<R>, for some type R.
  • T is of type future<R> or shared_future<R> All futures and shared_futures passed into when_all must be in a valid state (i.e. valid() == true).
Notes:
  • There are two variations of when_all. The first version takes a pair of InputIterators. The second takes any arbitrary number of future<R0> and shared_future<R1> objects, where R0 and R1 need not be the same type.
  • Calling the first signatureoverload of when_all where first == last, returns a future with an empty vector that is immediately ready.
  • Calling the second signatureoverload of when_allwhen_any with no arguments returns a future<tuple<>> that is immediately ready.
Remarks:
For the second overload, let Ui be decay_t<Fi> for each Fi in Futures. This function shall not participate in overload resolution unless each Ui is either future<Ri> or shared_future<Ri>.
Effects:
  • Each future and shared_future is waited upon and then copied into the collection of the output (returned) future, maintaining the order of the futures in the input collection. A new shared state containing a Sequence is created, where Sequence is either tuple or a vector based on the overload, as specified above. A new future object that refers to that shared state is created and returned from when_all.
  • Once all the futures and shared_futures supplied to the call to when_all are ready, the futures are moved, and the shared_futures are copied, into, correspondingly, futures or shared_futures of the futures member of Sequence in the shared state.
  • The order of the objects in the shared state matches the order of the arguments supplied to when_all.
  • The future returned by when_all will not throw an exception, but the futures and shared_futures held in the shared state may.
Postconditions:
  • valid() == true.
  • For all input futures, valid() == false.
  • For all inputoutput shared_futures, valid() == true.
Returns:
  • A future object that becomes ready when all the input futures/shared_futures are ready.
  • future<tuple<>> if when_all is called with zero arguments.
  • future<vector<future<R>>> if the input cardinality is unknown at compile and the iterator pair yields future<R>. R may be void. The order of the future in the output vector will be the same as given by the input iterator.
  • future<vector<shared_future<R>>> if the input cardinality is unknown at compile time and the iterator pair yields shared_future<R>. R may be void. The order of the future in the output vector will be the same as given by the input iterator.
  • future<tuple<future<R0>, future<R1>, future<R2>...>> if inputs are fixed in number. The inputs can be any arbitrary number of future and shared_future objects. The type of the element at each position of the tuple corresponds to the type of the argument at the same position. Any of R0, R1, R2, etc. maybe void.
3.8

Class template when_any_result

[futures.when_any_result]

The library provides a template for storing the result of when_any.


template<class Sequence>
struct when_any_result {
    size_t index;
    Sequence futures;
};
3.9

Function template when_any

[futures.when_any]

A new section 30.6.11 shall be inserted at the end of C++14 §30.6. Below is the content of that section.

The function template when_any creates a future object that becomes ready when at least one element in a set of future and shared_future objects becomes ready.


template <class InputIterator>
see below
future<when_any_result<vector<typename iterator_traits<InputIterator>::value_type>>>
when_any(InputIterator first, InputIterator last);

template <typenameclass... TFutures>
see below
future<when_any_result<tuple<decay_t<Futures>...>>> when_any(Futures&&... futures);

Requires:
  • For the first overload, iterator_traits<InputIterator>::value_type must be future<R> or shared_future<R>, for some type R.
  • T is of type future<R> or shared_future<R> All futures and shared_futures passed into when_any must be in a valid state (i.e. valid() == true).
Notes:
  • There are two variations of when_any. The first version takes a pair of InputIterators. The second takes any arbitrary number of future<R> and shared_future<R> objects, where R need not be the same type.
  • Calling the first signature of when_any where InputIterator first equals last, returns a future with an empty vector that is immediately ready.
  • Calling the second signature of when_any with no arguments returns a future<tuple<>> that is immediately ready.
  • Calling the first overload of when_any where first == last, returns a future that is immediately ready. The value of the index field of the when_any_result is unspecified. The futures field is an empty vector.
  • Calling the second overload of when_any with no arguments returns a future that is immediately ready. The value of the index field of the when_any_result is unspecified. The futures field is tuple<>.
Remarks:
For the second overload, let Ui be decay_t<Fi> for each Fi in Futures. This function shall not participate in overload resolution unless each Ui is either future<Ri> or shared_future<Ri>.
Effects:
  • Each future and shared_future is waited upon. When at least one is ready, all the futures are copied into the collection of the output (returned) future, maintaining the order of the futures in the input collection.
  • A new shared state containing when_any_result<Sequence> is created, where Sequence is a vector for the first overload and a tuple for the second overload. A new future object that refers to that shared state is created and returned from when_any.
  • Once at least one of the futures or shared_futures supplied to the call to when_any is ready, the futures are moved, and the shared_futures are copied into, correspondingly, futures or shared_futures of the futures member of Sequence in the shared state.
  • The order of the objects in the shared state matches the order of the arguments supplied to when_any.
  • The future returned by when_any will not throw an exception, but the futures and shared_futures held in the shared state may.
Postconditions:
  • valid() == true.
  • For all input futures, valid() == false.
  • For all inputoutput shared_futures, valid() == true.
Returns:
  • A future object that becomes ready when any of the input futures/shared_futures are ready.
  • future<tuple<>> if when_any is called with zero arguments.
  • future<vector<future<R>>> if the input cardinality is unknown at compile time and the iterator pair yields future<R>. R may be void. The order of the future in the output vector will be the same as given by the input iterator.
  • future<vector<shared_future<R>>> if the input cardinality is unknown at compile time and the iterator pair yields shared_future<R>. R may be void. The order of the future in the output vector will be the same as given by the input iterator.
  • future<tuple<future<R0>, future<R1>, future<R2>...>> if inputs are fixed in number. The inputs can be any arbitrary number of future and shared_future objects. The type of the element at each position of the tuple corresponds to the type of the argument at the same position. Any of R0, R1, R2, etc. maybe void.
3.10

Function template when_any_back

[futures.when_any_back]

A new section 30.6.12 shall be inserted at the end of C++14 §30.6. Below is the content of that section.


template <class InputIterator>
see below
when_any_back(InputIterator first, InputIterator last);
Effects:
  • Each future and shared_future is waited upon. When at least one is ready, all the future are copied into the collection of the output (returned) future.
  • The future returned by when_any_back will not throw an exception, but the futures and shared_futures held in the shared state may.
  • After the copy, the future or shared_future that was first detected as being ready swaps its position with that of the last element of the result collection, so that the ready future or shared_future may be identified in constant time. Only one future or shared_future is thus moved.
Postconditions:
  • valid() == true.
  • All input future<T>s valid() == false.
  • All input shared_future<T> valid() == true.
Returns:
  • future<vector<future<R>>> if the input cardinality is unknown at compile time and the iterator pair yields future<R>. R may be void.
  • future<vector<shared_future<R>>> if the input cardinality is unknown at compile time and the iterator pair yields shared_future<R>. R may be void.
3.11

Function template make_ready_future

[futures.make_ready_future]

A new section 30.6.13 shall be inserted at the end of C++14 §30.6. Below is the content of that section.


template <class T>
future<V> make_ready_future(T&& value);

future<void> make_ready_future();
  

Let U be decay_t<T>. Then V is X& if U equals reference_wrapper<X>, otherwise V is U.

Effects:
  • For the first overload, the value that is passed in to the function is moved to the shared state of the returned future if it is an rvalue. Otherwise the value is copied to the shared state of the returned future.
  • The second overload creates a shared state for the returned future.
Returns:
  • future<V>, if function is given a value of type T.
  • future<void>, if the function is not given any inputs.
Postconditions:
  • Returned future, valid() == true.
  • Returned future, is_ready() == true.
3.12

Function template make_exceptional_future

[futures.make_exceptional_future]

A new section 30.6.13 shall be inserted at the end of C++14 §30.6. Below is the content of that section.


template <class T>
future<T> make_exceptional_future(exception_ptr ex);
Effects:
Equivalent to

promise<T> p;
p.set_exception(ex);
return p.get_future();

template <class T, class E>
future<T> make_exceptional_future(E ex);
Effects:
Equivalent to

promise<T> p;
p.set_exception(make_exception_ptr(ex));
return p.get_future();
4

Acknowledgments

[acknowledgments]
The author is grateful to Agustín Bergé, Vicente J. Botet Escriba, Hartmut Kaiser, Stephan T. Lavavej and Anthony Williams for their contributions.