Document Number: | |
---|---|
Date: | |
Revises: | |
Editor: | Google DeepMind |
Note: this is an early draft. It’s known to be incomplet and incorrekt, and it has lots of bad formatting.
This technical specification describes extensions to the C++
Standard Library (
This technical specification is non-normative. Some of the library components in this technical specification may be considered for standardization in a future version of C++, but they are not currently part of any C++ standard. Some of the components in this technical specification may never be standardized, and others may be standardized in a substantially changed form.
The goal of this technical specification is to build more widespread existing practice for an expanded C++ standard library. It gives advice on extensions to those vendors who wish to provide them.
The following referenced document is indispensable for the application of this document. For dated references, only the edition cited applies. For undated references, the latest edition of the referenced document (including any amendments) applies.
ISO/IEC 14882:— is herein called the C++ Standard. References to clauses within the C++ Standard are written as "C++20 §3.2". The library described in ISO/IEC 14882:— clauses 16–32 is herein called the C++ Standard Library.
Unless otherwise specified, the whole of the C++ Standard's Library
introduction (
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:
::experimental::fundamentals_v3
to a namespace defined in the C++ Standard Library,
such as std
or std::chrono
, or
std
.
std::experimental::fundamentals_v3::pmr
because the C++ Standard Library defines std::pmr
.
— end example ]
Each header described in this technical
specification shall import the contents of
std::experimental::fundamentals_v3
into
std::experimental
as if by
namespace std::experimental::inline fundamentals_v3 {}
This technical specification also describes some experimental modifications to existing interfaces in the C++ Standard Library.
These modifications are described by quoting the affected parts of the standard
and using underlining to represent added text and strike-through to represent deleted text.
Unless otherwise specified, references to other entities
described in this technical specification are assumed to be
qualified with std::experimental::fundamentals_v3::
,
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
.
|
|
|
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 new versions of this
technical specification periodically, 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::fundamentals_v4
,
std::experimental::fundamentals_v5
, 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::fundamentals_vN
segment of its namespace and by removing the
experimental/
prefix from its header's path.
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.
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.)
Doc. No. | Title | Primary Section | Macro Name Suffix | Value | Header |
---|---|---|---|---|---|
N4388 | A Proposal to Add a Const-Propagating Wrapper to the Standard Library | propagate_const
| 201505 | <experimental/propagate_const>
| |
P0052R10 | Generic Scope Guard and RAII Wrapper for the Standard Library | scope
| 201902 | <experimental/scope>
| |
N3866 | Invocation type traits | invocation_type |
201406 | <experimental/type_traits> |
|
N4502 | The C++ Detection Idiom | detect |
201505 | <experimental/type_traits> |
|
N3916 | Type-erased allocator for std::function |
function_erased_allocator |
201406 |
<experimental/functional> ,
<experimental/utility> |
|
N4282 | The World’s Dumbest Smart Pointer | observer_ptr
| 201411 | <experimental/memory>
| |
N4257 | Delimited iterators | ostream_joiner |
201411 | <experimental/iterator> |
|
N3916 | Type-erased allocator for std::promise |
promise_erased_allocator |
201406 |
<experimental/future> ,
<experimental/utility> |
|
N3916 | Type-erased allocator for std::packaged_task |
packaged_task_erased_allocator |
201406 |
<experimental/future> ,
<experimental/utility> |
|
N3925 | A sample Proposal |
sample |
201402 | <experimental/algorithm> |
|
N4531 | std::rand replacement |
randint |
201511 | <experimental/random> |
Implementations that conform to this technical specification shall behave as if the modifications contained in this section are made to the C++ Standard.
The following changes to the library introduction allow the destructor
of scope_success
to throw exceptions.
16.5.4.8 Other functions [res.on.functions]
In certain cases […]
In particular, the effects are undefined in the following cases:
- […]
- if any replacement function or handler function or destructor operation exits via an exception, unless specifically allowed in the applicable Required behavior: or Throws: paragraph.
- if an incomplete type (6.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.
16.5.5.13 Restrictions on exception handling [res.on.exception.handling]
[…]
Functions from the C standard library shall not throw exceptions181 except when such a function calls a program-supplied function that throws an exception.182
Unless otherwise specified, destructor
Destructoroperations defined in the C++ standard library shall not throw exceptions. Every destructor without an exception specification in the C++ standard library shall behave as if it had a non-throwing exception specification.Functions defined in the C++ standard library […]
The following changes to the uses_allocator
trait and to the description of uses-allocator construction
allow a memory_resource
pointer act as an allocator in many circumstances.
20.10.8 uses_allocator [allocator.uses]
20.10.8.1 uses_allocator trait [allocator.uses.trait]
template <class T, class Alloc> struct uses_allocator;
- Remarks:
- Automatically detects whether
T
has a nestedallocator_type
that is convertible fromAlloc
. Meets theCpp17BinaryTypeTrait requirements (C++20 §20.15.1 ). The implementation shall provide a definition that is derived fromtrue_type
if a typeT::allocator_type
exists and eitheris_convertible_v<Alloc, T::allocator_type> != false
orT::allocator_type
is an alias forstd::experimental::erased_type
(3.1.2 ), otherwise it shall be derived fromfalse_type
. A program may specialize this template to derive fromtrue_type
for a user-defined typeT
that does not have a nestedallocator_type
but nonetheless can be constructed with an allocator where either:
- the first argument of a constructor has type
allocator_arg_t
and the second argument has typeAlloc
or- the last argument of a constructor has type
Alloc
.20.10.8.2 uses-allocator construction [allocator.uses.construction]
Uses-allocator construction with allocator
Alloc
refers to the construction of an objectobj
of typeT
, using constructor argumentsv1, v2, ..., vN
of typesV1, V2, ..., VN
, respectively, and an allocatoralloc
of typeAlloc
, whereAlloc
either (1) meets the requirements of an allocator (C++20 §16.5.3.5 ), or (2) is a pointer type convertible tostd::experimental::pmr::memory_resource*
(), according to the following rules:
<experimental/utility>
synopsis#include <utility>
namespace std::experimental::inline fundamentals_v3 {
// 3.1.2, Class erased_type
struct erased_type { };
} // namespace std::experimental::inline fundamentals_v3
erased_type
struct erased_type { };
The erased_type
struct
is an empty struct
that serves as a placeholder for a type T
in situations where the actual type T
is determined at runtime.
For example, the nested type, allocator_type
, is an alias for erased_type
in classes that use type-erased allocators (see
<experimental/propagate_const>
synopsisnamespace std {
namespace experimental::inline fundamentals_v3 {
// 3.2.2.1, Class template propagate_const overview
template <class T> class propagate_const;
// 3.2.2.8, propagate_const relational operators
template <class T>
constexpr bool operator==(const propagate_const<T>& pt, nullptr_t);
template <class T>
constexpr bool operator==(nullptr_t, const propagate_const<T>& pu);
template <class T>
constexpr bool operator!=(const propagate_const<T>& pt, nullptr_t);
template <class T>
constexpr bool operator!=(nullptr_t, const propagate_const<T>& pu);
template <class T, class U>
constexpr bool operator==(const propagate_const<T>& pt, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator!=(const propagate_const<T>& pt, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator<(const propagate_const<T>& pt, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator>(const propagate_const<T>& pt, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator<=(const propagate_const<T>& pt, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator>=(const propagate_const<T>& pt, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator==(const propagate_const<T>& pt, const U& u);
template <class T, class U>
constexpr bool operator!=(const propagate_const<T>& pt, const U& u);
template <class T, class U>
constexpr bool operator<(const propagate_const<T>& pt, const U& u);
template <class T, class U>
constexpr bool operator>(const propagate_const<T>& pt, const U& u);
template <class T, class U>
constexpr bool operator<=(const propagate_const<T>& pt, const U& u);
template <class T, class U>
constexpr bool operator>=(const propagate_const<T>& pt, const U& u);
template <class T, class U>
constexpr bool operator==(const T& t, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator!=(const T& t, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator<(const T& t, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator>(const T& t, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator<=(const T& t, const propagate_const<U>& pu);
template <class T, class U>
constexpr bool operator>=(const T& t, const propagate_const<U>& pu);
// 3.2.2.9, propagate_const specialized algorithms
template <class T>
constexpr void swap(propagate_const<T>& pt, propagate_const<T>& pt2) noexcept(see below);
// 3.2.2.10, propagate_const underlying pointer access
template <class T>
constexpr const T& get_underlying(const propagate_const<T>& pt) noexcept;
template <class T>
constexpr T& get_underlying(propagate_const<T>& pt) noexcept;
} // namespace experimental::inline fundamentals_v3
// 3.2.2.11, propagate_const hash support
template <class T> struct hash;
template <class T>
struct hash<experimental::fundamentals_v3::propagate_const<T>>;
// 3.2.2.12, propagate_const comparison function objects
template <class T> struct equal_to;
template <class T>
struct equal_to<experimental::fundamentals_v3::propagate_const<T>>;
template <class T> struct not_equal_to;
template <class T>
struct not_equal_to<experimental::fundamentals_v3::propagate_const<T>>;
template <class T> struct less;
template <class T>
struct less<experimental::fundamentals_v3::propagate_const<T>>;
template <class T> struct greater;
template <class T>
struct greater<experimental::fundamentals_v3::propagate_const<T>>;
template <class T> struct less_equal;
template <class T>
struct less_equal<experimental::fundamentals_v3::propagate_const<T>>;
template <class T> struct greater_equal;
template <class T>
struct greater_equal<experimental::fundamentals_v3::propagate_const<T>>;
} // namespace std
propagate_const
propagate_const
overviewnamespace std::experimental::inline fundamentals_v3 {
template <class T> class propagate_const {
public:
using element_type = remove_reference_t<decltype(*declval<T&>())>;
// 3.2.2.3, propagate_const constructors
constexpr propagate_const() = default;
propagate_const(const propagate_const& p) = delete;
constexpr propagate_const(propagate_const&& p) = default;
template <class U>
see below constexpr propagate_const(propagate_const<U>&& pu);
template <class U>
see below constexpr propagate_const(U&& u);
// 3.2.2.4, propagate_const assignment
propagate_const& operator=(const propagate_const& p) = delete;
constexpr propagate_const& operator=(propagate_const&& p) = default;
template <class U>
constexpr propagate_const& operator=(propagate_const<U>&& pu);
template <class U>
constexpr propagate_const& operator=(U&& u);
// 3.2.2.5, propagate_const const observers
explicit constexpr operator bool() const;
constexpr const element_type* operator->() const;
constexpr operator const element_type*() const; // Not always defined
constexpr const element_type& operator*() const;
constexpr const element_type* get() const;
// 3.2.2.6, propagate_const non-const observers
constexpr element_type* operator->();
constexpr operator element_type*(); // Not always defined
constexpr element_type& operator*();
constexpr element_type* get();
// 3.2.2.7, propagate_const modifiers
constexpr void swap(propagate_const& pt) noexcept(is_nothrow_swappable<T>);
private:
T t_; //exposition only
};
} // namespace std::experimental::inline fundamentals_v3
propagate_const
is a wrapper around a pointer-like object type T
which treats the wrapped pointer as a pointer to const
when
the wrapper is accessed through a const
access path.
propagate_const
requirements on T
T
shall be an object pointer type or a class type for which
decltype(*declval<T&>())
is an lvalue reference; otherwise
the program is ill-formed.
If T
is an array type, reference type, pointer to function type or
pointer to (possibly cv-qualified) void
, then the program is
ill-formed.
propagate_const<const int*>
is well-formed
— end note ]
propagate_const
requirements on class type T
If T
is class
type then it shall satisfy the following requirements. In this sub-clause
t
denotes a non-const
lvalue of type T
, ct
is a const T&
bound to t
, element_type
denotes
an object type.
T
and const T
shall be contextually convertible to bool
.
If T
is implicitly convertible to element_type*
,
(element_type*)t == t.get()
shall be true
.
If const T
is implicitly convertible to const element_type*
,
(const element_type*)ct == ct.get()
shall be true
.
Expression | Return type | Pre-conditions | Operational semantics |
---|---|---|---|
t.get() |
element_type* |
||
ct.get() |
const element_type* or element_type* |
|
t.get() == ct.get() . |
*t |
element_type& |
t.get() != nullptr |
*t refers to the same object as *(t.get()) |
*ct |
const element_type& or element_type& |
ct.get() != nullptr |
*ct refers to the same object as *(ct.get()) |
t.operator->() |
element_type* |
t.get() != nullptr |
t.operator->() == t.get() |
ct.operator->() |
const element_type* or element_type* |
ct.get() != nullptr |
ct.operator->() == ct.get() |
(bool)t |
bool |
|
(bool)t is equivalent to t.get() != nullptr |
(bool)ct |
bool |
|
(bool)ct is equivalent to ct.get() != nullptr |
propagate_const
constructors
explicit
. This is typically implemented by declaring two such
constructors, of which at most one participates in overload resolution.
— end note ]
template <class U>
see below constexpr propagate_const(propagate_const<U>&& pu);
is_constructible_v<T, U>
is true.
t_
as if
direct-non-list-initializing an object of type T
with the
expression std::move(pu.t_)
.
explicit
if and only if
!is_convertible_v<U&&, T>.
template <class U>
see below constexpr propagate_const(U&& u);
is_constructible_v<T, U>
is true
and decay_t<U>
is not a specialization of propagate_const
.
t_
as if
direct-non-list-initializing an object of type T
with
the expression std::forward<U>(u)
.
explicit
if and only if
!is_convertible_v<U&&, T>.
propagate_const
assignmenttemplate <class U>
constexpr propagate_const& operator=(propagate_const<U>&& pu);
U
is implicitly convertible to T
.
t_ = std::move(pu.t_)
.*this
.template <class U>
constexpr propagate_const& operator=(U&& u);
U
is implicitly convertible to T
and
decay_t<U>
is not a specialization of propagate_const
.
t_ = std::forward<U>(u)
.*this
.propagate_const
const observersexplicit constexpr operator bool() const;
(bool)t_
.constexpr const element_type* operator->() const;
get() != nullptr
.get()
.constexpr operator const element_type*() const;
T
is an object pointer type or
has an implicit conversion to const element_type*
.
get()
.constexpr const element_type& operator*() const;
get() != nullptr
.*get()
.constexpr const element_type* get() const;
t_
if T
is an object pointer type,
otherwise t_.get()
.
propagate_const
non-const observersconstexpr element_type* operator->();
get() != nullptr
.get()
.constexpr operator element_type*();
T
is an object pointer type or
has an implicit conversion to element_type*
.
get()
.constexpr element_type& operator*();
get() != nullptr
.*get()
.constexpr element_type* get();
t_
if T
is an object pointer type,
otherwise t_.get()
.
propagate_const
modifiersconstexpr void swap(propagate_const& pt) noexcept(is_nothrow_swappable<T>);
T
are swappable
(swap(t_, pt.t_)
.propagate_const
relational operatorstemplate <class T>
constexpr bool operator==(const propagate_const<T>& pt, nullptr_t);
pt.t_ == nullptr
.template <class T>
constexpr bool operator==(nullptr_t, const propagate_const<T>& pt);
nullptr == pt.t_
.template <class T>
constexpr bool operator!=(const propagate_const<T>& pt, nullptr_t);
pt.t_ != nullptr
.template <class T>
constexpr bool operator!=(nullptr_t, const propagate_const<T>& pt);
nullptr != pt.t_
.template <class T, class U>
constexpr bool operator==(const propagate_const<T>& pt, const propagate_const<U>& pu);
pt.t_ == pu.t_
.template <class T, class U>
constexpr bool operator!=(const propagate_const<T>& pt, const propagate_const<U>& pu);
pt.t_ != pu.t_
.template <class T, class U>
constexpr bool operator<(const propagate_const<T>& pt, const propagate_const<U>& pu);
pt.t_ < pu.t_
.template <class T, class U>
constexpr bool operator>(const propagate_const<T>& pt, const propagate_const<U>& pu);
pt.t_ > pu.t_
.template <class T, class U>
constexpr bool operator<=(const propagate_const<T>& pt, const propagate_const<U>& pu);
pt.t_ <= pu.t_
.template <class T, class U>
constexpr bool operator>=(const propagate_const<T>& pt, const propagate_const<U>& pu);
pt.t_ >= pu.t_
.template <class T, class U>
constexpr bool operator==(const propagate_const<T>& pt, const U& u);
pt.t_ == u
.template <class T, class U>
constexpr bool operator!=(const propagate_const<T>& pt, const U& u);
pt.t_ != u
.template <class T, class U>
constexpr bool operator<(const propagate_const<T>& pt, const U& u);
pt.t_ < u
.template <class T, class U>
constexpr bool operator>(const propagate_const<T>& pt, const U& u);
pt.t_ > u
.template <class T, class U>
constexpr bool operator<=(const propagate_const<T>& pt, const U& u);
pt.t_ <= u
.template <class T, class U>
constexpr bool operator>=(const propagate_const<T>& pt, const U& u);
pt.t_ >= u
.template <class T, class U>
constexpr bool operator==(const T& t, const propagate_const<U>& pu);
t == pu.t_
.template <class T, class U>
constexpr bool operator!=(const T& t, const propagate_const<U>& pu);
t != pu.t_
.template <class T, class U>
constexpr bool operator<(const T& t, const propagate_const<U>& pu);
t < pu.t_
.template <class T, class U>
constexpr bool operator>(const T& t, const propagate_const<U>& pu);
t > pu.t_
.template <class T, class U>
constexpr bool operator<=(const T& t, const propagate_const<U>& pu);
t <= pu.t_
.template <class T, class U>
constexpr bool operator>=(const T& t, const propagate_const<U>& pu);
t >= pu.t_
.propagate_const
specialized algorithmstemplate <class T>
constexpr void swap(propagate_const<T>& pt1, propagate_const<T>& pt2) noexcept(see below);
is_swappable_v<T>
is true
.pt1.swap(pt2)
.noexcept
is equivalent to:
noexcept(pt1.swap(pt2))
propagate_const
underlying pointer accessAccess to the underlying object pointer type is through free functions rather than member functions. These functions are intended to resemble cast operations to encourage caution when using them.
template <class T>
constexpr const T& get_underlying(const propagate_const<T>& pt) noexcept;
template <class T>
constexpr T& get_underlying(propagate_const<T>& pt) noexcept;
propagate_const
hash supporttemplate <class T>
struct hash<experimental::fundamentals_v3::propagate_const<T>>;
The specialization hash<experimental::fundamentals_v3::propagate_const<T>>
is enabled (hash<T>
is enabled.
When enabled, for an object p
of type propagate_const<T>
,
hash<experimental::fundamentals_v3::propagate_const<T>>()(p)
evaluates to the same value as hash<T>()(p.t_)
.
propagate_const
comparison function objectstemplate <class T>
struct equal_to<experimental::fundamentals_v3::propagate_const<T>>;
For objects p, q
of type propagate_const<T>
,
equal_to<experimental::fundamentals_v3::propagate_const<T>>()(p,
q)
shall evaluate to the same value as equal_to<T>()(p.t_,
q.t_)
.
equal_to<T>
is well-formed
equal_to<T>
is well-defined.
template <class T>
struct not_equal_to<experimental::fundamentals_v3::propagate_const<T>>;
For objects p, q
of type propagate_const<T>
,
not_equal_to<experimental::fundamentals_v3::propagate_const<T>>()(p, q)
shall evaluate to the same value as not_equal_to<T>()(p.t_, q.t_)
.
not_equal_to<T>
is well-formed
not_equal_to<T>
is well-defined.
template <class T>
struct less<experimental::fundamentals_v3::propagate_const<T>>;
For objects p, q
of type propagate_const<T>
,
less<experimental::fundamentals_v3::propagate_const<T>>()(p, q)
shall evaluate to the same value as less<T>()(p.t_, q.t_)
.
less<T>
is well-formed
less<T>
is well-defined.
template <class T>
struct greater<experimental::fundamentals_v3::propagate_const<T>>;
For objects p, q
of type propagate_const<T>
,
greater<experimental::fundamentals_v3::propagate_const<T>>()(p, q)
shall evaluate to the same value as greater<T>()(p.t_, q.t_)
.
greater<T>
is well-formed
greater<T>
is well-defined.
template <class T>
struct less_equal<experimental::fundamentals_v3::propagate_const<T>>;
For objects p, q
of type propagate_const<T>
,
less_equal<experimental::fundamentals_v3::propagate_const<T>>()(p, q)
shall evaluate to the same value as less_equal<T>()(p.t_, q.t_)
.
less_equal<T>
is well-formed
less_equal<T>
is well-defined.
template <class T>
struct greater_equal<experimental::fundamentals_v3::propagate_const<T>>;
For objects p, q
of type propagate_const<T>
,
greater_equal<experimental::fundamentals_v3::propagate_const<T>>()(p, q)
shall evaluate to the same value as greater_equal<T>()(p.t_, q.t_)
.
greater_equal<T>
is well-formed
greater_equal<T>
is well-defined.
<experimental/scope>
synopsisnamespace std::experimental::inline fundamentals_v3 {
// 3.3.2, Class templates scope_exit, scope_fail, and scope_success
template <class EF>
class scope_exit;
template <class EF>
class scope_fail;
template <class EF>
class scope_success;
// 3.3.3, Class template unique_resource
template <class R, class D>
class unique_resource;
// 3.3.3.6, unique_resource creation
template <class R, class D, class S=decay_t<R>>
unique_resource<decay_t<R>, decay_t<D>>
make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(see below);
} // namespace std::experimental::inline fundamentals_v3
scope_exit
, scope_fail
, and scope_success
The class templates scope_exit
, scope_fail
,
and scope_success
define scope guards that wrap a
function object to be called on their destruction.
In this subclause, the placeholder scope-guard
denotes each of these class templates. In descriptions of the
class members, scope-guard
refers to the enclosing
class.
namespace std::experimental::inline fundamentals_v3 {
template <class EF> class scope-guard {
public:
template <class EFP>
explicit scope-guard(EFP&& f) noexcept(see below);
scope-guard(scope-guard&& rhs) noexcept(see below);
scope-guard(const scope-guard&) = delete;
scope-guard& operator=(const scope-guard&) = delete;
scope-guard& operator=(scope-guard&&) = delete;
~scope-guard () noexcept(see below);
void release() noexcept;
private:
EF exit_function; // exposition only
bool execute_on_destruction{true}; // exposition only
int uncaught_on_creation{uncaught_exceptions()}; // exposition only
};
template <class EF>
scope-guard(EF) -> scope-guard<EF>;
} // namespace std::experimental::inline fundamentals_v3
The class template scope_exit
is a general-purpose
scope guard that calls its exit function when a scope is exited. The
class templates scope_fail
and scope_success
share the scope_exit
interface, only the situation when the
exit function is called differs.
void grow(vector<int>& v) {
scope_success guard([]{ cout << "Good!" << endl; });
v.resize(1024);
}
— end example ]
scope_success
or scope_exit
object refers to a local variable
of the function where it is defined, e.g., as a lambda capturing
the variable by reference, and that variable is used as a return
operand in that function, that variable might have already been
returned when the scope-guard
’s destructor
executes, calling the exit function. This can lead to surprising
behavior.
— end note ]
Template argument EF
shall be a function object type
(EF
is an object type, it shall meet the g
of type
remove_reference_t<EF>
, the expression
g()
shall be well-formed.
The constructor parameter f
in the following constructors
shall be a reference to a function or a reference to a function
object (
template <class EFP>
explicit scope-guard(EFP&& f) noexcept(
is_nothrow_constructible_v<EF, EFP> ||
is_nothrow_constructible_v<EF, EFP&>);
is_same_v<remove_cvref_t<EFP>,
scope-guard>
is false
and
is_constructible_v<EF, EFP>
is true
.
f()
is well-formed.
f()
has well-defined behavior.
For scope_exit
and scope_fail
,
calling f()
does not throw an exception.
EFP
is not an lvalue reference type and
is_nothrow_constructible_v<EF, EFP>
is true
, initialize exit_function
with std::forward<EFP>(f)
;
otherwise initialize exit_function
with f
.
For scope_exit
and scope_fail
,
if the initialization of exit_function
throws an exception,
calls f()
.
scope_success
, f()
will not be
called if the initialization fails.
— end note ]
exit_function
.
scope-guard(scope-guard&& rhs) noexcept(see below)
(is_nothrow_move_constructible_v<EF> || is_copy_constructible_v<EF>)
is true
.
EF
is an object type:
is_nothrow_move_constructible_v<EF>
is true
,
EF
meets the EF
meets the is_nothrow_move_constructible_v<EF>
is true
,
initializes exit_function
with std::forward<EF>(rhs.exit_function)
,
otherwise initializes exit_function
with rhs.exit_function
.
Initializes execute_on_destruction
from rhs.execute_on_destruction
and
uncaught_on_creation
from rhs.uncaught_on_creation
.
If construction succeeds, call rhs.release()
.
execute_on_destruction
yields the value rhs.execute_on_destruction
yielded before the construction. uncaught_on_creation
yields the value
rhs.uncaught_on_creation
yielded before the construction.
exit_function
.
noexcept
is equivalent to:
is_nothrow_move_constructible_v<EF> || is_nothrow_copy_constructible_v<EF>
~scope_exit() noexcept(true);
if (execute_on_destruction)
exit_function();
~scope_fail() noexcept(true);
if (execute_on_destruction && uncaught_exceptions() > uncaught_on_creation)
exit_function();
~scope_success() noexcept(noexcept(exit_function()));
if (execute_on_destruction && uncaught_exceptions() <= uncaught_on_creation)
exit_function();
noexcept(exit_function())
is false
,
exit_function()
may throw an exception,
notwithstanding the restrictions of exit_function()
.
void release() noexcept;
execute_on_destruction = false
.
unique_resource
namespace std::experimental::inline fundamentals_v3 {
template <class R, class D> class unique_resource {
public:
// 3.3.3.2, Constructors
unique_resource();
template <class RR, class DD>
unique_resource(RR&& r, DD&& d) noexcept(see below);
unique_resource(unique_resource&& rhs) noexcept(see below);
// 3.3.3.3, Destructor
~unique_resource();
// 3.3.3.4, Assignment
unique_resource& operator=(unique_resource&& rhs) noexcept(see below);
// 3.3.3.5, Other member functions
void reset() noexcept;
template <class RR>
void reset(RR&& r);
void release() noexcept;
const R& get() const noexcept;
see below operator*() const noexcept;
R operator->() const noexcept;
const D& get_deleter() const noexcept;
private:
using R1 = conditional_t<is_reference_v<R>, reference_wrapper<remove_reference_t<R>>, R>; // exposition only
R1 resource; // exposition only
D deleter; // exposition only
bool execute_on_reset{true}; // exposition only
};
template<class R, class D>
unique_resource(R, D) -> unique_resource<R, D>;
} // namespace std::experimental::inline fundamentals_v3
unique_resource
is a universal RAII wrapper for resource handles.
Typically, such resource handles are of trivial type and come with a factory function
and a clean-up or deleter function that do not throw exceptions. The clean-up function
together with the result of the creation function is used to create a unique_resource
variable, that on destruction will call the clean-up function. Access to the underlying
resource handle is achieved through get()
and in case of a pointer type
resource through a set of convenience pointer operator functions.
— end note ]
The template argument D
shall meet the requirements of a
d
of type D
and a lvalue r
of
type R
, the expression d(r)
shall be well-formed.
D
shall either meet the D
shall meet the is_nothrow_move_constructible_v<D>
shall be true
.
For the purpose of this subclause, a resource type T
is an object type that meets the requirements of is_nothrow_move_constructible_v<T>
is true
,
or is an lvalue reference to a resource type. R
shall be a resource type.
For the scope of the adjacent subclauses,
let RESOURCE
be defined as follows:
resource.get()
if is_reference_v<R>
is true
,resource
otherwise.unique_resource()
is_default_constructible_v<R> &&
is_default_constructible_v<D>
is true
.
resource
and deleter
;
execute_on_reset
is initialized with false
.
template <class RR, class DD>
unique_resource(RR&& r, DD&& d) noexcept(see below)
is_constructible_v<R1, RR> &&
is_constructible_v<D , DD> &&
(is_nothrow_constructible_v<R1, RR> || is_constructible_v<R1,RR&>) &&
(is_nothrow_constructible_v<D , DD> || is_constructible_v<D ,DD&>)
is true
.
R1
or D
is a specialization of reference_wrapper
.
— end note ]
d(r)
, d(RESOURCE)
and deleter(RESOURCE)
are well-formed.
d(r)
, d(RESOURCE)
or deleter(RESOURCE)
has well-defined behavior and
does not throw an exception.
is_nothrow_constructible_v<R1, RR>
is true
,
initializes resource
with std::forward<RR>(r)
,
otherwise initializes resource
with r
.
Then, if is_nothrow_constructible_v<D, DD>
is true,
initializes deleter
with std::forward<DD>(d)
,
otherwise initializes deleter
with d
.
If initialization of resource
throws an exception,
calls d(r)
.
If initialization of deleter
throws an exception, calls d(RESOURCE)
.
resource
or deleter
.
noexcept
is equivalent to:
(is_nothrow_constructible_v<R1, RR> || is_nothrow_constructible_v<R1, RR&>) &&
(is_nothrow_constructible_v<D , DD> || is_nothrow_constructible_v<D , DD&>)
unique_resource(unique_resource&& rhs) noexcept(see below);
resource
as follows:
is_nothrow_move_constructible_v<R1>
is
true
, from std::move(rhs.resource)
;rhs.resource
.resource
throws an exception,
rhs
is left owning the resource and will free it in due time.
— end note ]
deleter
as follows:
is_nothrow_move_constructible_v<D>
is
true
, from std::move(rhs.deleter)
;rhs.deleter
.deleter
throws an exception and
is_nothrow_move_constructible_v<R1>
is true
and rhs.execute_on_reset
is true:
rhs.deleter(RESOURCE);
rhs.release();
Finally, execute_on_reset
is initialized with
exchange(rhs.execute_on_reset, false)
.
noexcept
is equivalent to:
is_nothrow_move_constructible_v<R1> && is_nothrow_move_constructible_v<D>
~unique_resource();
reset()
.unique_resource& operator=(unique_resource&& rhs) noexcept(see below);
is_nothrow_move_assignable_v<R1>
is true
,
R1
meets the R1
meets the is_nothrow_move_assignable_v<D>
is true
,
D
meets the D
meets the reset();
if constexpr (is_nothrow_move_assignable_v<R1>) {
if constexpr (is_nothrow_move_assignable_v<D>) {
resource = std::move(rhs.resource);
deleter = std::move(rhs.deleter);
} else {
deleter = rhs.deleter;
resource = std::move(rhs.resource);
}
} else {
if constexpr (is_nothrow_move_assignable_v<D>) {
resource = rhs.resource;
deleter = std::move(rhs.deleter);
} else {
resource = rhs.resource;
deleter = rhs.deleter;
}
}
execute_on_reset = exchange(rhs.execute_on_reset, false);
rhs
intact and *this
in the released state.
— end note ]
noexcept
is equivalent to:
is_nothrow_move_assignable_v<R1> && is_nothrow_move_assignable_v<D>
void reset() noexcept;
if (execute_on_reset) {
execute_on_reset = false;
deleter(RESOURCE);
}
template <class RR> void reset(RR&& r);
resource
is well-formed.
deleter(r)
is well-formed.
deleter(r)
has well-defined behavior
and does not throw an exception.
reset();
if constexpr (is_nothrow_assignable_v<R1&, RR>) {
resource = std::forward<RR>(r);
} else {
resource = as_const(r);
}
execute_on_reset = true;
If copy-assignment of resource
throws an exception,
calls deleter(r)
.
void release() noexcept;
execute_on_reset = false
.const R& get() const noexcept;
resource
.see below operator*() const noexcept;
is_pointer_v<R>
is true
and
is_void_v<remove_pointer_t<R>>
is false
.
return *get();
add_lvalue_reference_t<remove_pointer_t<R>>
.
R operator->() const noexcept;
is_pointer_v<R>
is true
.
get()
.const D& get_deleter() const noexcept;
deleter
.unique_resource
creationtemplate <class R, class D, class S=decay_t<R>>
unique_resource<decay_t<R>, decay_t<D>>
make_unique_resource_checked(R&& resource, const S& invalid, D&& d)
noexcept(is_nothrow_constructible_v<decay_t<R>, R> &&
is_nothrow_constructible_v<decay_t<D>, D>);
(resource == invalid ? true : false)
is well-formed.
(resource == invalid ? true : false)
has well-defined behavior and does not throw an exception.
std::forward<R>(resource), std::forward<D>(d)
,
and !bool(resource == invalid)
.
Any failure during construction of the return value will not call d(resource)
if bool(resource == invalid)
is true
.
fclose
when fopen
fails.
auto file = make_unique_resource_checked(
::fopen("potentially_nonexistent_file.txt", "r"),
nullptr,
[](auto fptr){ ::fclose(fptr); });
— end example ]
#include <type_traits>
namespace std::experimental::inline fundamentals_v3 {
// 3.4.2, Other type transformations
template <class> class invocation_type; // not defined
template <class F, class... ArgTypes> class invocation_type<F(ArgTypes...)>;
template <class> class raw_invocation_type; // not defined
template <class F, class... ArgTypes> class raw_invocation_type<F(ArgTypes...)>;
template <class T>
using invocation_type_t = typename invocation_type<T>::type;
template <class T>
using raw_invocation_type_t = typename raw_invocation_type<T>::type;
// 3.4.3, Detection idiom
struct nonesuch;
template <template<class...> class Op, class... Args>
using is_detected = see below;
template <template<class...> class Op, class... Args>
inline constexpr bool is_detected_v
= is_detected<Op, Args...>::value;
template <template<class...> class Op, class... Args>
using detected_t = see below;
template <class Default, template<class...> class Op, class... Args>
using detected_or = see below;
template <class Default, template<class...> class Op, class... Args>
using detected_or_t = typename detected_or<Default, Op, Args...>::type;
template <class Expected, template<class...> class Op, class... Args>
using is_detected_exact = is_same<Expected, detected_t<Op, Args...>>;
template <class Expected, template<class...> class Op, class... Args>
inline constexpr bool is_detected_exact_v
= is_detected_exact<Expected, Op, Args...>::value;
template <class To, template<class...> class Op, class... Args>
using is_detected_convertible = is_convertible<detected_t<Op, Args...>, To>;
template <class To, template<class...> class Op, class... Args>
inline constexpr bool is_detected_convertible_v
= is_detected_convertible<To, Op, Args...>::value;
} // namespace std::experimental::inline fundamentals_v3
This sub-clause contains templates that may be used to transform one type to another following some predefined rule.
Each of the templates in this subclause shall be a
Within this section, define the invocation parameters of INVOKE(f, t1, t2, ..., tN)
as follows,
in which T1
is the possibly cv-qualified type of t1
and U1
denotes T1&
if t1
is an lvalue
or T1&&
if t1
is an rvalue:
f
is a pointer to a member function of a class T
the U1
followed by
the parameters of f
matched by t2
, ..., tN
.
N == 1
and f
is a pointer to member data of a class T
the U1
.
f
is a class object,
the t1
, ..., tN
of the best viable function (t1
, ..., tN
among the function call operators and surrogate call functions of f
.
f
matching t1
, ... tN
.
In all of the above cases,
if an argument tI
matches the ellipsis in the function's tI
.
S
is defined as
struct S {
int f(double const &) const;
void operator()(int, int);
void operator()(char const *, int i = 2, int j = 3);
void operator()(...);
};
INVOKE(&S::f, S(), 3.5)
are (S &&, double const &)
.INVOKE(S(), 1, 2)
are (int, int)
.INVOKE(S(), "abc", 5)
are (const char *, int)
.
The defaulted parameter j
does not correspond to an argument.INVOKE(S(), locale(), 5)
are (locale, int)
.
Arguments corresponding to ellipsis maintain their types.Template | Condition | Comments |
---|---|---|
template <class Fn, class... ArgTypes>
|
Fn and all types in the parameter pack ArgTypes
shall be complete types, (possibly cv-qualified) void , or arrays of unknown bound.
|
see below |
template <class Fn, class... ArgTypes>
|
Fn and all types in the parameter pack ArgTypes
shall be complete types, (possibly cv-qualified) void ,
or arrays of unknown bound.
|
see below |
Access checking is performed as if in a context unrelated to Fn
and ArgTypes
.
Only the validity of the immediate context of the expression is considered.
The member raw_invocation_type<Fn(ArgTypes...)>::type
shall be defined as follows.
If the expression INVOKE(declval<Fn>(), declval<ArgTypes>()...)
is ill-formed when treated as an unevaluated operand (type
. Otherwise:
R
denote result_of_t<Fn(ArgTypes...)>
.Ti
be the INVOKE(declval<Fn>(), declval<ArgTypes>()...)
.type
shall name the function type R(T1, T2, ...)
.
The member invocation_type<Fn(ArgTypes...)>::type
shall be defined as follows.
If raw_invocation_type<Fn(ArgTypes...)>::type
does not exist, there shall be no member type
.
Otherwise:
A1, A2,
… denote ArgTypes...
R(T1, T2, …)
denote raw_invocation_type_t<Fn(ArgTypes...)>
type
shall name the function type R(U1, U2, …)
where Ui
is decay_t<Ai>
if declval<Ai>()
is an rvalue
otherwise Ti
.
struct nonesuch {
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};
nonesuch
has no default constructor
(
template <class Default, class AlwaysVoid,
template<class...> class Op, class... Args>
struct DETECTOR { // exposition only
using value_t = false_type;
using type = Default;
};
template <class Default, template<class...> class Op, class... Args>
struct DETECTOR<Default, void_t<Op<Args...>>, Op, Args...> { // exposition only
using value_t = true_type;
using type = Op<Args...>;
};
template <template<class...> class Op, class... Args>
using is_detected = typename DETECTOR<nonesuch, void, Op, Args...>::value_t;
template <template<class...> class Op, class... Args>
using detected_t = typename DETECTOR<nonesuch, void, Op, Args...>::type;
template <class Default, template<class...> class Op, class... Args>
using detected_or = DETECTOR<Default, void, Op, Args...>;
// archetypal helper alias for a copy assignment operation:
template <class T>
using copy_assign_t = decltype(declval<T&>() = declval<T const &>());
// plausible implementation for the is_assignable type trait:
template <class T>
using is_copy_assignable = is_detected<copy_assign_t, T>;
// plausible implementation for an augmented is_assignable type trait
// that also checks the return type:
template <class T>
using is_canonical_copy_assignable = is_detected_exact<T&, copy_assign_t, T>;
— end example ]
// archetypal helper alias for a particular type member:
template <class T>
using diff_t = typename T::difference_type;
// alias the type member, if it exists, otherwise alias ptrdiff_t:
template <class Ptr>
using difference_type = detected_or_t<ptrdiff_t, diff_t, Ptr>;
— end example ]
<experimental/functional>
synopsis#include <functional>
namespace std {
namespace experimental::inline fundamentals_v3 {
// 4.2, Class template function
template<class> class function; // undefined
template<class R, class... ArgTypes> class function<R(ArgTypes...)>;
template<class R, class... ArgTypes>
void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&);
template<class R, class... ArgTypes>
bool operator==(const function<R(ArgTypes...)>&, nullptr_t) noexcept;
template<class R, class... ArgTypes>
bool operator==(nullptr_t, const function<R(ArgTypes...)>&) noexcept;
template<class R, class... ArgTypes>
bool operator!=(const function<R(ArgTypes...)>&, nullptr_t) noexcept;
template<class R, class... ArgTypes>
bool operator!=(nullptr_t, const function<R(ArgTypes...)>&) noexcept;
} // namespace experimental::inline fundamentals_v3
template<class R, class... ArgTypes, class Alloc>
struct uses_allocator<experimental::function<R(ArgTypes...)>, Alloc>;
} // namespace std
function
The specification of all declarations within this sub-clause std::experimental::function
uses
std::bad_function_call
, there is no additional type std::experimental::bad_function_call
— end note ]
namespace std {
namespace experimental::inline fundamentals_v3 {
template<class> class function; // undefined
template<class R, class... ArgTypes>
class function<R(ArgTypes...)> {
public:
using result_type = R;
using argument_type = T1;
using first_argument_type T1;
using second_argument_type = T2;
using allocator_type = erased_type;
function() noexcept;
function(nullptr_t) noexcept;
function(const function&);
function(function&&);
template<class F> function(F);
template<class A> function(allocator_arg_t, const A&) noexcept;
template<class A> function(allocator_arg_t, const A&,
nullptr_t) noexcept;
template<class A> function(allocator_arg_t, const A&,
const function&);
template<class A> function(allocator_arg_t, const A&,
function&&);
template<class F, class A> function(allocator_arg_t, const A&, F);
function& operator=(const function&);
function& operator=(function&&);
function& operator=(nullptr_t) noexcept;
template<class F> function& operator=(F&&);
template<class F> function& operator=(reference_wrapper<F>);
~function();
void swap(function&);
explicit operator bool() const noexcept;
R operator()(ArgTypes...) const;
const type_info& target_type() const noexcept;
template<class T> T* target() noexcept;
template<class T> const T* target() const noexcept;
pmr::memory_resource* get_memory_resource() const noexcept;
};
template <class R, class... ArgTypes>
bool operator==(const function<R(ArgTypes...)>&, nullptr_t) noexcept;
template <class R, class... ArgTypes>
bool operator==(nullptr_t, const function<R(ArgTypes...)>&) noexcept;
template <class R, class... ArgTypes>
bool operator!=(const function<R(ArgTypes...)>&, nullptr_t) noexcept;
template <class R, class... ArgTypes>
bool operator!=(nullptr_t, const function<R(ArgTypes...)>&) noexcept;
template <class R, class... ArgTypes>
void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&);
} // namespace experimental::inline fundamentals_v3
template <class R, class... ArgTypes, class Alloc>
struct uses_allocator<experimental::function<R(ArgTypes...)>, Alloc>
: true_type { };
} // namespace std
function
construct/copy/destroy
When a function
constructor that takes a first argument of type allocator_arg_t
is invoked,
the second argument is treated as a experimental::function
class template,
then that move or copy is performed by get_memory_resource()
.
In the following descriptions, let ALLOCATOR_OF(f)
be the allocator specified in the construction of function
f
,
or the value of experimental::
at the time of the construction of f
if no allocator was specified.
function& operator=(const function& f);
function(allocator_arg, ALLOCATOR_OF(*this), f).swap(*this);
*this
.function& operator=(function&& f);
function(allocator_arg, ALLOCATOR_OF(*this), std::move(f)).swap(*this);
*this
.function& operator=(nullptr_t) noexcept;
*this != nullptr
, destroys the target of this
.!(*this)
.
The memory resource returned by get_memory_resource()
after the assignment is equivalent to the memory resource before the assignment.
get_memory_resource()
might change
— end note ]
*this
.template<class F> function& operator=(F&& f);
declval<decay_t<F>&>()
is ArgTypes...
and return type R
.
function(allocator_arg, ALLOCATOR_OF(*this), std::forward<F>(f)).swap(*this);
*this
.template<class F> function& operator=(reference_wrapper<F> f);
function(allocator_arg, ALLOCATOR_OF(*this), f).swap(*this);
*this
.function
modifiersvoid swap(function& other);
*this->get_memory_resource() == *other.get_memory_resource()
.*this
and other
.*this
and other
are not interchanged.#include <memory>
namespace std {
namespace experimental::inline fundamentals_v3 {
// 5.2, Non-owning pointers
template <class W> class observer_ptr;
// 5.2.6, observer_ptr specialized algorithms
template <class W>
void swap(observer_ptr<W>&, observer_ptr<W>&) noexcept;
template <class W>
observer_ptr<W> make_observer(W*) noexcept;
// (in)equality operators
template <class W1, class W2>
bool operator==(observer_ptr<W1>, observer_ptr<W2>);
template <class W1, class W2>
bool operator!=(observer_ptr<W1>, observer_ptr<W2>);
template <class W>
bool operator==(observer_ptr<W>, nullptr_t) noexcept;
template <class W>
bool operator!=(observer_ptr<W>, nullptr_t) noexcept;
template <class W>
bool operator==(nullptr_t, observer_ptr<W>) noexcept;
template <class W>
bool operator!=(nullptr_t, observer_ptr<W>) noexcept;
// ordering operators
template <class W1, class W2>
bool operator<(observer_ptr<W1>, observer_ptr<W2>);
template <class W1, class W2>
bool operator>(observer_ptr<W1>, observer_ptr<W2>);
template <class W1, class W2>
bool operator<=(observer_ptr<W1>, observer_ptr<W2>);
template <class W1, class W2>
bool operator>=(observer_ptr<W1>, observer_ptr<W2>);
} // namespace experimental::inline fundamentals_v3
// 5.2.7, observer_ptr hash support
template <class T> struct hash;
template <class T> struct hash<experimental::observer_ptr<T>>;
} // namespace std
A non-owning pointer, known as an observer, is an object o
that stores a pointer to a second object, w
.
In this context, w
is known as a watched object.
nullptr
.
— end note ]
o
and w
.
Specializations of observer_ptr
shall meet the requirements
of a W
of an observer_ptr
shall not be a reference type, but may be an incomplete type.
observer_ptr
include clarity of interface specification in new code,
and interoperability with pointer-based legacy code.
— end note ]
observer_ptr
overviewnamespace std::experimental::inline fundamentals_v3 {
template <class W> class observer_ptr {
using pointer = add_pointer_t<W>; // exposition-only
using reference = add_lvalue_reference_t<W>; // exposition-only
public:
// publish our template parameter and variations thereof
using element_type = W;
// 5.2.2, observer_ptr constructors
// default constructor
constexpr observer_ptr() noexcept;
// pointer-accepting constructors
constexpr observer_ptr(nullptr_t) noexcept;
constexpr explicit observer_ptr(pointer) noexcept;
// copying constructors (in addition to the implicit copy constructor)
template <class W2> constexpr observer_ptr(observer_ptr<W2>) noexcept;
// 5.2.3, observer_ptr observers
constexpr pointer get() const noexcept;
constexpr reference operator*() const;
constexpr pointer operator->() const noexcept;
constexpr explicit operator bool() const noexcept;
// 5.2.4, observer_ptr conversions
constexpr explicit operator pointer() const noexcept;
// 5.2.5, observer_ptr modifiers
constexpr pointer release() noexcept;
constexpr void reset(pointer = nullptr) noexcept;
constexpr void swap(observer_ptr&) noexcept;
}; // observer_ptr<>
} // namespace std::experimental::inline fundamentals_v3
observer_ptr
constructorsconstexpr observer_ptr() noexcept; constexpr observer_ptr(nullptr_t) noexcept;
get() == nullptr
.constexpr explicit observer_ptr(pointer other) noexcept;
get() == other
.template <class W2> constexpr observer_ptr(observer_ptr<W2> other) noexcept;
W2*
is convertible to W*
.get() == other.get()
.observer_ptr
observersconstexpr pointer get() const noexcept;
constexpr reference operator*() const;
get() != nullptr
is true
.*get()
.constexpr pointer operator->() const noexcept;
get()
.constexpr explicit operator bool() const noexcept;
get() != nullptr
.observer_ptr
conversionsconstexpr explicit operator pointer() const noexcept;
get()
.observer_ptr
modifiersconstexpr pointer release() noexcept;
get() == nullptr
.get()
had at the start of the call to release
.constexpr void reset(pointer p = nullptr) noexcept;
get() == p
.constexpr void swap(observer_ptr& other) noexcept;
swap
on the stored pointers of *this
and other
.observer_ptr
specialized algorithmstemplate <class W>
void swap(observer_ptr<W>& p1, observer_ptr<W>& p2) noexcept;
p1.swap(p2)
.template <class W> observer_ptr<W> make_observer(W* p) noexcept;
observer_ptr<W>{p}
.template <class W1, class W2>
bool operator==(observer_ptr<W1> p1, observer_ptr<W2> p2);
p1.get() == p2.get()
.template <class W1, class W2>
bool operator!=(observer_ptr<W1> p1, observer_ptr<W2> p2);
not (p1 == p2)
.template <class W>
bool operator==(observer_ptr<W> p, nullptr_t) noexcept; template <class W>
bool operator==(nullptr_t, observer_ptr<W> p) noexcept;
not p
.template <class W>
bool operator!=(observer_ptr<W> p, nullptr_t) noexcept; template <class W>
bool operator!=(nullptr_t, observer_ptr<W> p) noexcept;
(bool)p
.template <class W1, class W2>
bool operator<(observer_ptr<W1> p1, observer_ptr<W2> p2);
less<W3>()(p1.get(), p2.get())
,
where W3
is the composite pointer type (W1*
and W2*
.
template <class W1, class W2>
bool operator>(observer_ptr<W1> p1, observer_ptr<W2> p2);
p2 < p1
.template <class W1, class W2>
bool operator<=(observer_ptr<W1> p1, observer_ptr<W2> p2);
not (p2 < p1)
.template <class W1, class W2>
bool operator>=(observer_ptr<W1> p1, observer_ptr<W2> p2);
not (p1 < p2)
.observer_ptr
hash supporttemplate <class T> struct hash<experimental::observer_ptr<T>>;
The specialization is enabled (p
of type observer_ptr<T>
,
hash<observer_ptr<T>>()(p)
evaluates to the same value as hash<T*>()(p.get())
.
A type-erased allocator is an allocator or memory resource, alloc
,
used to allocate internal data structures for an object X
of type C
,
but where C
is not dependent on the type of alloc
.
Once alloc
has been supplied to X
(typically as a constructor argument),
alloc
can be retrieved from X
only as a pointer rptr
of static type std::pmr::memory_resource*
(rptr
is computed from alloc
depends on the type of alloc
as described in
If the type of alloc is |
then the value of rptr is |
---|---|
non-existent — no alloc specified |
The value of pmr::get_default_resource() at the time of construction. |
nullptr_t |
The value of pmr::get_default_resource() at the time of construction. |
a pointer type convertible to pmr::memory_resource* |
static_cast<pmr::memory_resource*>(alloc) |
pmr::polymorphic_allocator<U> |
alloc.resource() |
any other type meeting the |
a pointer to a value of type pmr::resource_adaptor<A> where A is the type of alloc .
rptr remains valid only for the lifetime of X . |
None of the above | The program is ill-formed. |
Additionally, class C
meets the following requirements:
C::allocator_type
denotes std::experimental::erased_type
.X.get_memory_resource()
returns rptr
.<experimental/memory_resource>
synopsisnamespace std::experimental::inline fundamentals_v3::pmr {
// The name resource_adaptor_imp is for exposition only.
template <class Allocator> class resource_adaptor_imp;
template <class Allocator>
using resource_adaptor = resource_adaptor_imp<
typename allocator_traits<Allocator>::template rebind_alloc<char>>;
} // namespace std::experimental::inline fundamentals_v3::pmr
resource_adaptor
resource_adaptor
An instance of resource_adaptor<Allocator>
is an adaptor that wraps a memory_resource
interface around Allocator
.
In order that resource_adaptor<X<T>>
and resource_adaptor<X<U>>
are the same type for any allocator template X
and types T
and U
,
resource_adaptor<Allocator>
is rendered as an alias to a class template such that Allocator
is rebound to a char
value type in every specialization of the class template.
The requirements on this class template are defined below.
The name resource_adaptor_imp
is for exposition only and is not normative,
but the definitions of the members of that class, whatever its name, are normative.
In addition to the resource_adaptor
shall meet the following additional requirements:
typename allocator_traits<Allocator>::pointer
shall be identical to typename allocator_traits<Allocator>::value_type*
.typename allocator_traits<Allocator>::const_pointer
shall be identical to typename allocator_traits<Allocator>::value_type const*
.typename allocator_traits<Allocator>::void_pointer
shall be identical to void*
.typename allocator_traits<Allocator>::const_void_pointer
shall be identical to void const*
.
// The name resource_adaptor_imp is for exposition only.
template <class Allocator>
class resource_adaptor_imp : public memory_resource {
// for exposition only
Allocator m_alloc;
public:
using allocator_type = Allocator;
resource_adaptor_imp() = default;
resource_adaptor_imp(const resource_adaptor_imp&) = default;
resource_adaptor_imp(resource_adaptor_imp&&) = default;
explicit resource_adaptor_imp(const Allocator& a2);
explicit resource_adaptor_imp(Allocator&& a2);
resource_adaptor_imp& operator=(const resource_adaptor_imp&) = default;
allocator_type get_allocator() const { return m_alloc; }
protected:
virtual void* do_allocate(size_t bytes, size_t alignment);
virtual void do_deallocate(void* p, size_t bytes, size_t alignment);
virtual bool do_is_equal(const memory_resource& other) const noexcept;
};
template <class Allocator>
using resource_adaptor = typename resource_adaptor_imp<
typename allocator_traits<Allocator>::template rebind_alloc<char>>;
resource_adaptor_imp
constructorsexplicit resource_adaptor_imp(const Allocator& a2);
m_alloc
with a2
.explicit resource_adaptor_imp(Allocator&& a2);
m_alloc
with std::move(a2)
.resource_adaptor_imp
member functionsvoid* do_allocate(size_t bytes, size_t alignment);
m_alloc.allocate
.
The size and alignment of the allocated memory shall meet the requirements
for a class derived from memory_resource
(void do_deallocate(void* p, size_t bytes, size_t alignment);
p
was previously allocated using A.allocate
, where A == m_alloc
, and not subsequently deallocated.m_alloc.deallocate()
.bool do_is_equal(const memory_resource& other) const noexcept;
Let p
be dynamic_cast<const resource_adaptor_imp*>(&other)
.
false
if p
is null, otherwise the value of m_alloc == p->m_alloc
.<experimental/iterator>
synopsis#include <iterator>
namespace std::experimental::inline fundamentals_v3 {
// 6.2, Class template ostream_joiner
template <class DelimT, class charT = char, class traits = char_traits<charT> >
class ostream_joiner;
template <class charT, class traits, class DelimT>
ostream_joiner<decay_t<DelimT>, charT, traits>
make_ostream_joiner(basic_ostream<charT, traits>& os, DelimT&& delimiter);
} // namespace std::experimental::inline fundamentals_v3
ostream_joiner
ostream_joiner
writes (using operator<<
) successive elements onto the output stream from which it was constructed.
The delimiter that it was constructed with is written to the stream between every two T
s that are written.
It is not possible to get a value out of the output iterator.
Its only use is as an output iterator in situations like
while (first != last)
*result++ = *first++;
ostream_joiner
is defined as
namespace std::experimental::inline fundamentals_v3 {
template <class DelimT, class charT = char, class traits = char_traits<charT> >
class ostream_joiner {
public:
using char_type = charT;
using traits_type = traits;
using ostream_type = basic_ostream<charT, traits>;
using iterator_category = output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
ostream_joiner(ostream_type& s, const DelimT& delimiter);
ostream_joiner(ostream_type& s, DelimT&& delimiter);
template<typename T>
ostream_joiner& operator=(const T& value);
ostream_joiner& operator*() noexcept;
ostream_joiner& operator++() noexcept;
ostream_joiner& operator++(int) noexcept;
private:
ostream_type* out_stream; // exposition only
DelimT delim; // exposition only
bool first_element; // exposition only
};
} // namespace std::experimental::inline fundamentals_v3
ostream_joiner
constructorostream_joiner(ostream_type& s, const DelimT& delimiter);
out_stream
with std::addressof(s)
,
delim
with delimiter
,
and first_element
with true
.
ostream_joiner(ostream_type& s, DelimT&& delimiter);
out_stream
with std::addressof(s)
,
delim
with move(delimiter)
,
and first_element
with true
.
ostream_joiner
operationstemplate<typename T>
ostream_joiner& operator=(const T& value);
if (!first_element)
*out_stream << delim;
first_element = false;
*out_stream << value;
return *this;
ostream_joiner& operator*() noexcept;
*this
.ostream_joiner& operator++() noexcept; ostream_joiner& operator++(int) noexcept;
*this
.ostream_joiner
creation functiontemplate <class charT, class traits, class DelimT>
ostream_joiner<decay_t<DelimT>, charT, traits>
make_ostream_joiner(basic_ostream<charT, traits>& os, DelimT&& delimiter);
ostream_joiner<decay_t<DelimT>, charT, traits>(os, forward<DelimT>(delimiter));
#include <future>
namespace std {
namespace experimental::inline fundamentals_v3 {
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> 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;
} // namespace experimental::inline fundamentals_v3
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
promise
The specification of all declarations within this sub-clause
namespace std {
namespace experimental::inline fundamentals_v3 {
template <class R>
class promise {
public:
using allocator_type = erased_type;
promise();
template <class Allocator>
promise(allocator_arg_t, const Allocator& a);
promise(promise&& rhs) noexcept;
promise(const promise& rhs) = delete;
~promise();
promise& operator=(promise&& rhs) noexcept;
promise& operator=(const promise& rhs) = delete;
void swap(promise& other) noexcept;
future<R> get_future();
void set_value(see below);
void set_exception(exception_ptr p);
void set_value_at_thread_exit(const R& r);
void set_value_at_thread_exit(see below);
void set_exception_at_thread_exit(exception_ptr p);
pmr::memory_resource* get_memory_resource() const noexcept;
};
template <class R>
void swap(promise<R>& x, promise<R>& y) noexcept;
} // namespace experimental::inline fundamentals_v3
template <class R, class Alloc>
struct uses_allocator<experimental::promise<R>, Alloc>;
} // namespace std
When a promise
constructor that takes a first argument of type allocator_arg_t
is invoked,
the second argument is treated as a type-erased allocator (
packaged_task
The specification of all declarations within this sub-clause
namespace std {
namespace experimental::inline fundamentals_v3 {
template <class R, class... ArgTypes>
class packaged_task<R(ArgTypes...)> {
public:
using allocator_type = erased_type;
packaged_task() noexcept;
template <class F>
explicit packaged_task(F&& f);
template <class F, class Allocator>
explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f);
~packaged_task();
packaged_task(const packaged_task&) = delete;
packaged_task& operator=(const packaged_task&) = delete;
packaged_task(packaged_task&& rhs) noexcept;
packaged_task& operator=(packaged_task&& rhs) noexcept;
void swap(packaged_task& other) noexcept;
bool valid() const noexcept;
future<R> get_future();
void operator()(ArgTypes... );
void make_ready_at_thread_exit(ArgTypes...);
void reset();
pmr::memory_resource* get_memory_resource() const noexcept;
};
template <class R, class... ArgTypes>
void swap(packaged_task<R(ArgTypes...)>&, packaged_task<R(ArgTypes...)>&) noexcept;
} // namespace experimental::inline fundamentals_v3
template <class R, class Alloc>
struct uses_allocator<experimental::packaged_task<R>, Alloc>;
} // namespace std
When a packaged_task
constructor that takes a first argument of type allocator_arg_t
is invoked,
the second argument is treated as a type-erased allocator (
<experimental/algorithm>
synopsis#include <algorithm>
namespace std::experimental::inline fundamentals_v3 {
// 8.2, Sampling
template<class PopulationIterator, class SampleIterator, class Distance>
SampleIterator sample(PopulationIterator first, PopulationIterator last,
SampleIterator out, Distance n);
// 8.3, Shuffle
template<class RandomAccessIterator>
void shuffle(RandomAccessIterator first, RandomAccessIterator last);
} // namespace std::experimental::inline fundamentals_v3
template<class PopulationIterator, class SampleIterator, class Distance>
SampleIterator sample(PopulationIterator first, PopulationIterator last,
SampleIterator out, Distance n);
return ::std::sample(first, last, out, n, g);
where g
denotes
the per-thread engine (g
serves as the
implementation’s source of randomness.
template<class RandomAccessIterator>
void shuffle(RandomAccessIterator first, RandomAccessIterator last);
RandomAccessIterator
meets the
[first,last)
such that each possible permutation of those elements has equal
probability of appearance.(last - first) - 1
swaps.<experimental/random>
synopsis#include <random>
namespace std::experimental::inline fundamentals_v3 {
// 9.1.2, Function template randint
template <class IntType>
IntType randint(IntType a, IntType b);
void reseed();
void reseed(default_random_engine::result_type value);
} // namespace std::experimental::inline fundamentals_v3
randint
A separate per-thread engine of type default_random_engine
(
template<class IntType>
IntType randint(IntType a, IntType b);
IntType
in a
≤ b
.a
≤ i ≤ b
,
produced from a thread-local instance of uniform_int_distribution<IntType>
(void reseed(); void reseed(default_random_engine::result_type value);
g
be the per-thread engine. The first
form sets g
to an unpredictable state. The second form
invokes g.seed(value)
.randint
do not
depend on values produced by g
before calling reseed
.
reseed
also resets any instances of uniform_int_distribution
used by randint
.
— end note ]