Document Number: | |
---|---|
Date: | |
Revises: | |
Editor: | Google, Inc. |
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++14 §3.2". The library described in ISO/IEC 14882:— clauses 17–30 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_v2
to a namespace defined in the C++ Standard Library,
such as std
or std::chrono
, or
std
.
std::experimental::fundamentals_v2::chrono
because the C++ Standard Library defines std::chrono
.
This TS does not define std::pmr::experimental::fundamentals_v2
because the C++ Standard Library does not define std::pmr
.
— end example ]
Each header described in this technical
specification shall import the contents of
std::experimental::fundamentals_v2
into
std::experimental
as if by
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {}
}
}
Unless otherwise specified, references to other entities
described in this technical specification are assumed to be
qualified with std::experimental::fundamentals_v2::
,
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 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::fundamentals_v3
,
std::experimental::fundamentals_v4
, 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
| 201504 | <experimental/propagate_const>
| |
N4076 | A proposal to add a generalized callable negator | not_fn
| 201406 | <experimental/functional>
| |
N4282 | The World’s Dumbest Smart Pointer | observer_ptr
| 201411 | <experimental/memory>
| |
N4273 | Uniform Container Erasure | erase_if |
201411 | <experimental/vector> |
|
N4315 | make_array | make_array |
201504 | <experimental/array> |
|
N4257 | Delimited iterators | ostream_joiner |
201411 | <experimental/iterator> |
|
N4061 | Greatest Common Divisor and Least Common Multiple | gcd_lcm |
201411 | <experimental/numeric> |
propagate_const
propagate_const general
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.
<experimental/propagate_const>
synopsisnamespace std {
namespace experimental {
inline namespace fundamentals_v2 {
template <class T> class propagate_const {
public:
typedef remove_reference_t<decltype(*declval<T&>())> element_type;
// 2.1.4, 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);
// 2.1.5, 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);
// 2.1.6, 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;
// 2.1.7, propagate_const non-const observers
constexpr element_type* operator->();
constexpr operator element_type*(); // Not always defined
constexpr element_type& operator*();
constexpr element_type* get();
// 2.1.8, propagate_const modifiers
constexpr void swap(propagate_const& pt) noexcept(see below);
private:
T t_; //exposition only
};
// 2.1.9, 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);
// 2.1.10, propagate_const specialized algorithms
template <class T>
constexpr void swap(propagate_const<T>& pt, propagate_const<T>& pt2) noexcept(see below);
// 2.1.11, 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;
} // inline namespace fundamentals_v2
} // namespace experimental
// 2.1.12, propagate_const hash support
template <class T> struct hash;
template <class T>
struct hash<experimental::fundamentals_v2::propagate_const<T>>;
// 2.1.13, propagate_const comparison function objects
template <class T> struct equal_to;
template <class T>
struct equal_to<experimental::fundamentals_v2::propagate_const<T>>;
template <class T> struct not_equal_to;
template <class T>
struct not_equal_to<experimental::fundamentals_v2::propagate_const<T>>;
template <class T> struct less;
template <class T>
struct less<experimental::fundamentals_v2::propagate_const<T>>;
template <class T> struct greater;
template <class T>
struct greater<experimental::fundamentals_v2::propagate_const<T>>;
template <class T> struct less_equal;
template <class T>
struct less_equal<experimental::fundamentals_v2::propagate_const<T>>;
template <class T> struct greater_equal;
template <class T>
struct greater_equal<experimental::fundamentals_v2::propagate_const<T>>;
} // namespace std
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&&>
.
The constructor is specified as explicit
if and only if
!is_convertible_v<U&&, T>.
t_
as if
direct-non-list-initializing an object of type T
with the
expression std::move(pu.t_)
.
template <class U>
see below constexpr propagate_const(U&& u)
is_constructible_v<T, U&&>
and decay_t<U>
is not a specialization of propagate_const
.
The constructor is specified as explicit
if and only if
!is_convertible_v<U&&, T>.
t_
as if
direct-non-list-initializing an object of type T
with
the expression std::forward<U>(u)
.
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
get()
.T
is an object pointer type or
has an implicit conversion to const element_type*
.
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*()
get()
.T
is an object pointer type or
has an implicit conversion to element_type*
.
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(see below)
The constant-expression in the exception-specification is noexcept(swap(t_, pt.t_))
.
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<U>& 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)
The constant-expression in the exception-specification is noexcept(swap(pt1.t_, pt2.t_))
.
swap(pt1.t_, pt2.t_)
.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_v2::propagate_const<T>>
For an object p
of type propagate_const<T>
,
hash<experimental::fundamentals_v2::propagate_const<T>>()(p)
shall evaluate to the same value as hash<T>()(p.t_)
.
hash<T>
shall be well-formed and well-defined,
and shall meet the requirements of class template hash.
propagate_const
comparison function objectstemplate <class T>
struct equal_to<experimental::fundamentals_v2::propagate_const<T>>
For objects p, q
of type propagate_const<T>
,
equal_to<experimental::fundamentals_v2::propagate_const<T>>()(p,
q)
shall evaluate to the same value as equal_to<T>()(p.t_,
q.t_)
.
equal_to<T>
shall be well-formed and well-defined.
template <class T>
struct not_equal_to<experimental::fundamentals_v2::propagate_const<T>>
For objects p, q
of type propagate_const<T>
,
not_equal_to<experimental::fundamentals_v2::propagate_const<T>>()(p, q)
shall evaluate to the same value as not_equal_to<T>()(p.t_, q.t_)
.
not_equal_to<T>
shall be well-formed and well-defined.
template <class T>
struct less<experimental::fundamentals_v2::propagate_const<T>>
For objects p, q
of type propagate_const<T>
,
less<experimental::fundamentals_v2::propagate_const<T>>()(p,
q)
shall evaluate to the same value as less<T>()(p.t_,
q.t_)
.
less<T>
shall be well-formed and well-defined.
template <class T>
struct greater<experimental::fundamentals_v2::propagate_const<T>>
For objects p, q
of type propagate_const<T>
,
greater<experimental::fundamentals_v2::propagate_const<T>>()(p,
q)
shall evaluate to the same value as greater<T>()(p.t_,
q.t_)
.
greater<T>
shall be well-formed and well-defined.
template <class T>
struct less_equal<experimental::fundamentals_v2::propagate_const<T>>
For objects p, q
of type propagate_const<T>
,
less_equal<experimental::fundamentals_v2::propagate_const<T>>()(p,
q)
shall evaluate to the same value as less_equal<T>()(p.t_,
q.t_)
.
less_equal<T>
shall be well-formed and well-defined.
template <class T>
struct greater_equal<experimental::fundamentals_v2::propagate_const<T>>
For objects p, q
of type propagate_const<T>
,
greater_equal<experimental::fundamentals_v2::propagate_const<T>>()(p,
q)
shall evaluate to the same value as greater_equal<T>()(p.t_, q.t_)
.
greater_equal<T>
shall be well-formed and well-defined.
<experimental/functional>
synopsis#include <functional>
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// 3.2, Function template not_fn
template <class F> unspecified not_fn(F&& f);
} // namespace fundamentals_v2
} // namespace experimental
} // namespace std
not_fn
template <class F> unspecified not_fn(F&& f);
In the text that follows:
FD
is the type decay_t<F>
,fd
is an lvalue of type FD
constructed from std::forward<F>(f),
fn
is a forwarding call wrapper created as a result of not_fn(f)
,is_constructible<FD, F>::value
shall be true
.
fd
shall be a callable object (fn
such that the expression fn(a1, a2, ..., aN)
is equivalent to !INVOKE(fd, a1, a2, ..., aN)
(fd
throws an exception.MoveConstructible
.
If FD
satisfies the requirements of CopyConstructible
, then
the return type shall satisfy the requirements of CopyConstructible
.
FD
is MoveConstructible.
— end note ]
not_fn
can usually provide a better solution than using the negators not1
and not2
— end note ]
#include <memory>
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// 4.2, Non-owning pointers
template <class W> class observer_ptr;
// 4.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>);
} // inline namespace fundamentals_v2
} // namespace experimental
// 4.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 CopyConstructible
and CopyAssignable
type.
The template parameter 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 {
namespace experimental {
inline namespace fundamentals_v2 {
template <class W> class observer_ptr {
public:
// publish our template parameter and variations thereof
using element_type = W;
using pointer = add_pointer_t<W>; // exposition-only
using reference = add_lvalue_reference_t<W>; // exposition-only
// 4.2.2, observer_ptr constructors
// default c’tor
constexpr observer_ptr() noexcept;
// pointer-accepting c’tors
constexpr observer_ptr(nullptr_t) noexcept;
constexpr explicit observer_ptr(pointer) noexcept;
// copying c’tors (in addition to compiler-generated copy c’tor)
template <class W2> constexpr observer_ptr(observer_ptr<W2>) noexcept;
// 4.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;
// 4.2.4, observer_ptr conversions
constexpr explicit operator pointer() const noexcept;
// 4.2.5, observer_ptr modifiers
constexpr pointer release() noexcept;
constexpr void reset(pointer = nullptr) noexcept;
constexpr void swap(observer_ptr&) noexcept;
}; // observer_ptr<>
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
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;
get() == other.get()
.W2*
is convertible to W*
.observer_ptr
observersconstexpr pointer get() const noexcept;
constexpr reference operator*() const;
get() != nullptr
.*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 W>
bool operator>(observer_ptr<W> p1, observer_ptr<W> p2);
p2 < p1
.template <class W>
bool operator<=(observer_ptr<W> p1, observer_ptr<W> p2);
not (p2 < p1)
.template <class W>
bool operator>=(observer_ptr<W> p1, observer_ptr<W> p2);
not (p1 < p2)
.observer_ptr
hash supporttemplate <class T> struct hash<experimental::observer_ptr<T>>;
The template specialization shall meet the requirements of class template hash
(p
of type observer_ptr<T>
,
hash<observer_ptr<T>>()(p)
shall evaluate to the same value as hash<T*>()(p.get())
.
For brevity, this section specifies the contents of 9 headers,
each of which behaves as described by
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// 5.1.2, Function template erase_if
// 5.1.3, Function template erase
// <experimental/string>
template <class charT, class traits, class A, class Predicate>
void erase_if(basic_string<charT, traits, A>& c, Predicate pred);
template <class charT, class traits, class A, class U>
void erase(basic_string<charT, traits, A>& c, const U& value);
// <experimental/deque>
template <class T, class A, class Predicate>
void erase_if(deque<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(deque<T, A>& c, const U& value);
// <experimental/vector>
template <class T, class A, class Predicate>
void erase_if(vector<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(vector<T, A>& c, const U& value);
// <experimental/forward_list>
template <class T, class A, class Predicate>
void erase_if(forward_list<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(forward_list<T, A>& c, const U& value);
// <experimental/list>
template <class T, class A, class Predicate>
void erase_if(list<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(list<T, A>& c, const U& value);
// <experimental/map>
template <class K, class T, class C, class A, class Predicate>
void erase_if(map<K, T, C, A>& c, Predicate pred);
template <class K, class T, class C, class A, class Predicate>
void erase_if(multimap<K, T, C, A>& c, Predicate pred);
// <experimental/set>
template <class K, class C, class A, class Predicate>
void erase_if(set<K, C, A>& c, Predicate pred);
template <class K, class C, class A, class Predicate>
void erase_if(multiset<K, C, A>& c, Predicate pred);
// <experimental/unordered_map>
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred);
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred);
// <experimental/unordered_set>
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_set<K, H, P, A>& c, Predicate pred);
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred);
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
erase_if
template <class charT, class traits, class A, class Predicate>
void erase_if(basic_string<charT, traits, A>& c, Predicate pred); template <class T, class A, class Predicate>
void erase_if(deque<T, A>& c, Predicate pred); template <class T, class A, class Predicate>
void erase_if(vector<T, A>& c, Predicate pred);
c.erase(remove_if(c.begin(), c.end(), pred), c.end());
template <class T, class A, class Predicate>
void erase_if(forward_list<T, A>& c, Predicate pred); template <class T, class A, class Predicate>
void erase_if(list<T, A>& c, Predicate pred);
c.remove_if(pred);
template <class K, class T, class C, class A, class Predicate>
void erase_if(map<K, T, C, A>& c, Predicate pred); template <class K, class T, class C, class A, class Predicate>
void erase_if(multimap<K, T, C, A>& c, Predicate pred); template <class K, class C, class A, class Predicate>
void erase_if(set<K, C, A>& c, Predicate pred); template <class K, class C, class A, class Predicate>
void erase_if(multiset<K, C, A>& c, Predicate pred); template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred); template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred); template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_set<K, H, P, A>& c, Predicate pred); template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred);
for (auto i = c.begin(), last = c.end(); i != last; ) {
if (pred(*i)) {
i = c.erase(i);
} else {
++i;
}
}
template <class charT, class traits, class A, class U>
void erase(basic_string<charT, traits, A>& c, const U& value); template <class T, class A, class U>
void erase(deque<T, A>& c, const U& value); template <class T, class A, class U>
void erase(vector<T, A>& c, const U& value);
c.erase(remove(c.begin(), c.end(), value), c.end());
template <class T, class A, class U>
void erase(forward_list<T, A>& c, const U& value); template <class T, class A, class U>
void erase(list<T, A>& c, const U& value);
erase_if(c, [&](auto& elem) { return elem == value; });
erase()
for associative containers and unordered associative containers are intentionally not provided.
— end note ]
array
#include <array>
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// 5.2.2, Array creation functions
template <class D = void, class... Types>
constexpr array<VT, sizeof...(Types)> make_array(Types&&... t);
template <class T, size_t N>
constexpr array<remove_cv_t<T>, N> to_array(T (&a)[N]);
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
template <class D = void, class... Types>
constexpr array<VT, sizeof...(Types)> make_array(Types&&... t);
Let Ui be decay_t<
Ti>
for each Ti in Types
.
D
is void
and at least one Ui is a specialization of reference_wrapper
.
array<VT, sizeof...(Types)>{ std::forward<Types>(t)... }
, where VT
is common_type_t<Types...>
if D
is void
, otherwise VT
is D
.
int i = 1; int& ri = i;
auto a1 = make_array(i, ri); // a1 is of type array<int, 2>
auto a2 = make_array(i, ri, 42L); // a2 is of type array<long, 3>
auto a3 = make_array<long>(i, ri); // a3 is of type array<long, 2>
auto a4 = make_array<long>(); // a4 is of type array<long, 0>
auto a5 = make_array(); // ill-formed
— end example ]
template <class T, size_t N>
constexpr array<remove_cv_t<T>, N> to_array(T (&a)[N]);
array<remove_cv_t<T>, N>
such that each element is copy-initialized with the corresponding element of a
.
<experimental/iterator>
synopsis#include <iterator>
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// 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);
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
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 {
namespace experimental {
inline namespace fundamentals_v2 {
template <class DelimT, class charT = char, class traits = char_traits<charT> >
class ostream_joiner {
public:
typedef charT char_type;
typedef traits traits_type;
typedef basic_ostream<charT,traits> ostream_type;
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
ostream_joiner(ostream_type& s, DelimT&& delimiter);
ostream_joiner(ostream_type& s, const DelimT& delimiter);
template<typename T>
ostream_joiner<DelimT, charT,traits>& operator=(const T& value);
ostream_joiner<DelimT, charT,traits>& operator*();
ostream_joiner<DelimT, charT,traits>& operator++();
ostream_joiner<DelimT, charT,traits>& operator++(int);
private:
basic_ostream<charT,traits>* out_stream; // exposition only
DelimT delim; // exposition only
bool first_element; // exposition only
};
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
ostream_joiner
constructorostream_joiner(ostream_type& s, const DelimT& delimiter);
out_stream
with &s
,
delim
with delimiter
,
and first_element
with true
.
ostream_joiner(ostream_type& s, DelimT&& delimiter);
out_stream
with &s
,
delim
with move(delimiter)
,
and first_element
with true
.
ostream_joiner
operationstemplate<typename T>
ostream_joiner<DelimT, charT, traits>& operator=(const T& value);
if (!first_element)
*out_stream << delim;
first_element = false;
*out_stream << value;
return (*this);
ostream_joiner<DelimT, charT, traits>& operator*();
*this
ostream_joiner<DelimT, charT, traits>& operator++(); ostream_joiner<DelimT, charT, traits>& operator++(int);
*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 <numeric>
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// 7.1.2, Greatest common divisor
template<class M, class N>
constexpr common_type_t<M,N> gcd(M m, N n);
// 7.1.3, Least common multiple
template<class M, class N>
constexpr common_type_t<M,N> lcm(M m, N n);
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
template<class M, class N>
constexpr common_type_t<M,N> gcd(M m, N n);
|m|
shall be representable as a value of type M
and
|n|
shall be representable as a value of type N
.
gcd(m, m) = |m|
is representable as a value of type M
.
— end note ]
M
or N
is not an integer type, the program is ill-formed.m
and n
are both zero.
Otherwise, returns the greatest common divisor of |m|
and |n|
.template<class M, class N>
constexpr common_type_t<M,N> lcm(M m, N n);
|m|
shall be representable as a value of type M
and
|n|
shall be representable as a value of type N
.
The least common multiple of |m|
and |n|
shall be representable as a value of type common_type_t<M,N>
.
M
or N
is not an integer type, the program is ill-formed.m
or n
is zero.
Otherwise, returns the least common multiple of |m|
and |n|
.