fill_n
and swap_ranges
from <algorithm>
optional
now specified in terms of **this
instead of .value()
variant
now specified in terms of get_if
instead of get
string_view
find / search operations now specified in terms of data_
instead of at()
size()
call in string_view::ends_with
// freestanding
// hosted
vs. // freestanding-deleted
// freestanding-delete
to // freestanding-deleted
bad_optional_access
per LWG's request for consistencystring_view::starts_with
and string_view::ends_with
in terms of freestanding functions// freestanding-deleted
// mostly freestanding
header markerstring_view::contains
All of the added classes are fundamentally compatible with freestanding, except for a few methods that throw (e.g. array::at). We explicitly =delete these undesirable methods.
The main driving factor for these additions is the immense usefulness of these types in practice.
Since we aren’t changing the semantics of any of the classes (except deleted non-critical methods), it is fair to say that all of the (implementer and user) experience gathered as part of hosted applies the same to freestanding.
The only question is, whether these classes are compatible with freestanding. To which the answer is yes! For example, the [Embedded Template Library] offers direct mappings of the std types. Even in kernel-level libraries, like Serenity’s [AK] use a form of these utilities.
Our decision to delete methods we can’t mark as freestanding was made to keep overload resolution the same on freestanding as hosted.
An additional benefit here is, that users of these classes, who might expect to use a throwing method, which was not provided by the implementation, will get a more meaningful error than the method simply missing. This also means we can keep options open for reintroducing the deleted functions into freestanding. (e.g. operator<<(ostream, string_view), should <ostream> be added).
The predecessor to this paper used //freestanding, partial
to mean a class (template) is only required to be partially implemented, in conjunction with //freestanding, omit
meaning a declaration is not in freestanding.
In this paper, we mark not fully freestanding classes templates as // freestanding-partial
, and use P2338's // freestanding-deleted
to mark which pieces of the class should be omitted.
We no longer annotate all the class members, favoring terseness over explicitness.
In this paper, we mark std::visit as freestanding, even though it is theoretically throwing. However, the conditions for std::visit to throw are as follows:
It is possible for a variant to hold no value if an exception is thrown during a type-changing assignment or emplacement.
This means a variant will only throw on visit if a user type throws (library types don’t throw on freestanding). In this case, std::visit throwing isn’t a problem, since the user’s code is already using, and (hopefully) handling exceptions.
This however has the unfortunate side-effect that we need to keep bad_variant_access freestanding.
By getting rid of std::get, we force users to use std::get_if. Since std::get_if returns a pointer, one can only access the value of a variant by dereferencing said pointer, obtaining an lvalue, discarding the value category of the held object. This is unlikely to have an impact on application code, but might impact highly generic library code.
std::forward_like can help in these cases. The value category of the variant can be transferred to the dereferenced pointer returned from set::get_if.
<algorithm>
inclusionsstd::array::fill
is specified in terms of std::fill_n
.
std::array::swap
is specified in terms of std::swap_ranges
.
Both fill_n
and swap_ranges
are reasonable inclusions in freestanding.
Rather than respecify array
in terms of other freestanding facilities, we have chosen to pull in the functions from <algorithm>
that we need.
A later paper will likely add many more facilities from <algorithm>
.
An email on the LEWG reflector in June 2023 asked to add std::fill_n
and std::swap_ranges
.
The request received eleven +1's, and no opposition to this change.
The authors did not vote on the reflector poll.
Unless otherwise specified, the requirements on freestanding items for a freestanding implementation are the same as the corresponding requirements for a hosted implementation, except that not all of the members of the namespaces are required to be present.[Note 1:This implies that freestanding item enumerations have the same enumerators on freestanding implementations and hosted implementations.Furthermore, class types have the same members and class templates have the same deduction guides on freestanding implementations and hosted implementations.— end note]Function declarations and function template declarations followed by a comment that include freestanding-deleted are freestanding deleted functions.On freestanding implementations, it is implementation defined whether each function definition introduced by a freestanding deleted function isa freestanding item ora deleted function ([dcl.fct.def.delete]) or whether the requirements are the same as the corresponding requirements for a hosted implementation..[ Note: Deleted definitions ensure that overload resolution does not silently change when migrating a library from a freestanding implementation to a hosted implementation. -end note][ Example:double abs(double j); // freestanding-deleted
-end example]A class type declaration or class template declaration in a synopsis that is followed by a comment that includes freestanding-partial is a freestanding item, except that it contains at least one freestanding deleted function.[ Example:template <class T, size_t N> struct array; //freestanding-partial template<class T, size_t N> struct array { constexpr reference operator[](size_type n); constexpr const_reference operator[](size_type n) const; constexpr reference at(size_type n); //freestanding-deleted constexpr const_reference at(size_type n) const; //freestanding-deleted };-end example]A declaration in aheadersynopsis is a freestanding item if
- it is followed by a comment that includes freestanding,
or- it is followed by a comment that includes freestanding-deleted,
- it is followed by a comment that includes freestanding-partial,
- the header synopsis begins with a comment that includes all freestanding
., or- the header synopsis begins with a comment that includes mostly freestanding and the declaration is not followed by a comment that includes hosted.
[ Note: Declarations followed by hosted in mostly freestanding headers are not freestanding items. Overload resolution with such functions could vary between hosted and freestanding implementations. -end note]An entity, deduction guide, or typedef-name is a freestanding item if it is:
- introduced by a declaration that is a freestanding item,
- a member of a freestanding item,
- an enumerator of a freestanding item,
- a deduction guide of a freestanding item,
- an enclosing namespace of a freestanding item,
- a friend of a freestanding item,
- denoted by a typedef-name that is a freestanding item, or
- denoted by an alias template that is a freestanding item.
A macro is a freestanding item if it is defined in a header synopsis and
- the definition is followed by a comment that includes freestanding,
or- the header synopsis begins with a comment that includes all freestanding
., or- the header synopsis begins with a comment that includes mostly freestanding and the definition is not followed by a comment that includes hosted.
Subclause | Header(s) | |
---|---|---|
[…] | […] | […] |
?.? [optional] | Optional objects | <optional> |
?.? [variant] | Variants | <variant> |
?.? [string.view] | String view classes | <string_view> |
?.? [array] | Class template array |
<array> |
?.? [algorithms] | Algorithms library | <algorithm> |
[…] | […] | […] |
#define __cpp_lib_freestanding_algorithm 20XXXXL // freestanding, also in <algorithm> #define __cpp_lib_freestanding_array 20XXXXL // freestanding, also in <array> #define __cpp_lib_freestanding_optional 20XXXXL // freestanding, also in <optional> #define __cpp_lib_freestanding_string_view 20XXXXL // freestanding, also in <string_view> #define __cpp_lib_freestanding_variant 20XXXXL // freestanding, also in <variant>
Please insert a // mostly freestanding
comment at the beginning of the [optional.syn] synopsis.
// freestanding-partial
comment to the following declaration:
optional
Instructions to the editor:
Please append a // freestanding-deleted
comment to every overload of value
.
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;
Let U be invoke_result_t<F, decltype (value()**this)>.Mandates: remove_cvref_t<U> is a specialization of optional.Effects: Equivalent to: if (*this) { return invoke(std::forward<F>(f),value()**this); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto and_then(F&& f) &&; template<class F> constexpr auto and_then(F&& f) const &&;
Let U be invoke_result_t<F, decltype(std::move(value()**this))>.Mandates: remove_cvref_t<U> is a specialization of optional.Effects: Equivalent to: if (*this) { return invoke(std::forward<F>(f), std::move(value()**this)); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto transform(F&& f) &; template<class F> constexpr auto transform(F&& f) const &;
Let U be remove_cv_t<invoke_result_t<F, decltype(value()**this)>>.Mandates: U is a non-array object type other than in_place_t or nullopt_t.The declaration U u(invoke(std::forward<F>(f),value()**this)); is well-formed for some invented variable u.[Note 1:There is no requirement that U is movable ([dcl.init.general]).— end note]Returns: If *this contains a value, an optional<U> object whose contained value is direct-non-list-initialized with invoke(std::forward<F>(f),value()**this); otherwise, optional<U>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;
Let U be remove_cv_t<invoke_result_t<F, decltype(std::move(value()**this))>>.Mandates: U is a non-array object type other than in_place_t or nullopt_t.The declaration U u(invoke(std::forward<F>(f), std::move(value()**this))); is well-formed for some invented variable u.[Note 2:There is no requirement that U is movable ([dcl.init.general]).— end note]Returns: If *this contains a value, an optional<U> object whose contained value is direct-non-list-initialized with invoke(std::forward<F>(f), std::move(value()**this)); otherwise, optional<U>().
Please insert a // mostly freestanding
comment at the beginning of the [variant.syn] synopsis.
Please append a // freestanding-deleted
comment to every get
overload in the synopsis.
constexpr variant(const variant& w);
constexpr variant(variant&& w) noexcept(see below);
constexpr variant& operator=(const variant& rhs);
Effects:
Otherwise, if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value. Otherwise, if either is_nothrow_copy_constructible_v<> is true or is_nothrow_move_constructible_v<> is false, equivalent to emplace<j>(get<j>(rhs)*get_if<j>(addressof(rhs))). Otherwise, equivalent to operator=(variant(rhs)).constexpr variant& operator=(variant&& rhs) noexcept(see below);
Effects:
Otherwise, if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value. Otherwise, if index() == j, assignsget<j>(std::move(rhs))std::move(*get_if<j>(addressof(rhs))) to the value contained in *this. Otherwise, equivalent to emplace<j>(get<j>(std::move(rhs))std::move(*get_if<j>(addressof(rhs)))).
constexpr void swap(variant& rhs) noexcept(see below);
Remarks: If an exception is thrown during the call to function swap(get<i>(*this)*get_if<i>(this),get<i>(rhs)*get_if<i>(addressof(rhs))), the states of the contained values of *this and of rhs are determined by the exception safety guarantee of swap for lvalues of with i being index().If an exception is thrown during the exchange of the values of *this and rhs, the states of the values of *this and of rhs are determined by the exception safety guarantee of variant's move constructor.The exception specification is equivalent to the logical and of is_nothrow_move_constructible_v<> && is_nothrow_swappable_v<> for all i.
Replace every instance of get<i>(v) in [variant.relops] with *get_if<i>(addressof(v)).
Replace every instance of get<i>(w) in [variant.relops] with *get_if<i>(addressof(w)).
For each valid pack m, let e(m) denote the expression:INVOKE(std::forward<Visitor>(vis), get<m>(std::forward<V>(vars))...) // see [func.require]INVOKE(std::forward<Visitor>(vis), forward_like<V>(*get_if<m>(addressof(vars)))...) // see [func.require] for the first form andINVOKE<R>(std::forward<Visitor>(vis), get<m>(std::forward<V>(vars))...) // see [func.require]INVOKE<R>(std::forward<Visitor>(vis), forward_like<V>(*get_if<m>(addressof(vars)))...) // see [func.require] for the second form.
Please insert a // mostly freestanding
comment at the beginning of the [string.view.synop] synopsis.
// hosted
comment to the following declaration:
operator<<
// freestanding-partial
comment to the following declaration:
basic_string_view
// freestanding-deleted
to the following functions:
at
copy
substr
compare(size_type pos1, size_type n1, basic_string_view s)
compare(size_type pos1, size_type n1, basic_string_view s, size_type pos2, size_type n2)
compare(size_type pos1, size_type n1, const charT* s)
compare(size_type pos1, size_type n1, const charT* s, size_type n2)
Note that the compare(basic_string_view str) const
and compare(const charT* s) const
overloads are intentionally not freestanding-deleted.
basic_string_view
overload of starts_with
so that it doesn't reference freestanding-deleted methods.
constexpr bool starts_with(basic_string_view x) const noexcept;
Please modify the basic_string_view
overload of ends_with
so that it doesn't reference freestanding-deleted methods.
constexpr bool ends_with(basic_string_view x) const noexcept;
Replace every instance of at(xpos + I) in [string.view.find] with data_[xpos + I].
Replace every instance of at(xpos) in [string.view.find] with data_[xpos].
Replace every instance of str.at(I) in [string.view.find] with str.data_[I].
Please insert a // mostly freestanding
comment at the beginning of the [array.syn] synopsis.
Please append a // freestanding-partial
comment to array
// freestanding-deleted
comment to every overload of at
.
Please append a // freestanding
comment to the following functions:
fill_n(OutputIterator first, Size n, const T& value)
swap_ranges(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2)