Files
2025-10-25 03:02:53 +03:00

474 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[smartptr.adapt]
# 20 Memory management library [[mem]](./#mem)
## 20.3 Smart pointers [[smartptr]](smartptr#adapt)
### 20.3.4 Smart pointer adaptors [smartptr.adapt]
#### [20.3.4.1](#out.ptr.t) Class template out_ptr_t [[out.ptr.t]](out.ptr.t)
[1](#out.ptr.t-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5423)
out_ptr_t is a class template used to adapt types
such as smart pointers ([[smartptr]](smartptr "20.3Smart pointers"))
for functions that use output pointer parameters[.](#out.ptr.t-1.sentence-1)
[2](#out.ptr.t-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5428)
[*Example [1](#out.ptr.t-example-1)*:
#include <memory>#include <cstdio>int fopen_s(std::FILE** f, const char* name, const char* mode);
struct fclose_deleter {void operator()(std::FILE* f) const noexcept { std::fclose(f); }};
int main(int, char*[]) {constexpr const char* file_name = "ow.o";
std::unique_ptr<std::FILE, fclose_deleter> file_ptr; int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr), file_name, "r+b"); if (err != 0)return 1; // *file_ptr is validreturn 0;}unique_ptr can be used with out_ptr to be passed into an output pointer-style function,
without needing to hold onto an intermediate pointer value and
manually delete it on error or failure[.](#out.ptr.t-2.sentence-1)
— *end example*]
[🔗](#lib:out_ptr_t)
namespace std {template<class Smart, class Pointer, class... Args>class out_ptr_t {public:constexpr explicit out_ptr_t(Smart&, Args...);
out_ptr_t(const out_ptr_t&) = delete; constexpr ~out_ptr_t(); constexpr operator Pointer*() const noexcept; operator void**() const noexcept; private: Smart& s; // *exposition only* tuple<Args...> a; // *exposition only* Pointer p; // *exposition only*};}
[3](#out.ptr.t-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5480)
Pointer shall meet the [*Cpp17NullablePointer*](nullablepointer.requirements#:Cpp17NullablePointer "16.4.4.4Cpp17NullablePointer requirements[nullablepointer.requirements]") requirements[.](#out.ptr.t-3.sentence-1)
If Smart is a specialization of shared_ptr andsizeof...(Args) == 0,
the program is ill-formed[.](#out.ptr.t-3.sentence-2)
[*Note [1](#out.ptr.t-note-1)*:
It is typically a user error to reset a shared_ptr without specifying a deleter,
as shared_ptr will replace a custom deleter upon usage of reset,
as specified in [[util.smartptr.shared.mod]](util.smartptr.shared.mod "20.3.2.2.5Modifiers")[.](#out.ptr.t-3.sentence-3)
— *end note*]
[4](#out.ptr.t-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5492)
Program-defined specializations of out_ptr_t that depend on at least one program-defined type
need not meet the requirements for the primary template[.](#out.ptr.t-4.sentence-1)
[5](#out.ptr.t-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5497)
Evaluations of the conversion functions
on the same object may conflict ([[intro.races]](intro.races "6.10.2.2Data races"))[.](#out.ptr.t-5.sentence-1)
[🔗](#lib:out_ptr_t,constructor)
`constexpr explicit out_ptr_t(Smart& smart, Args... args);
`
[6](#out.ptr.t-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5507)
*Effects*: Initializes s with smart,a with std::forward<Args>(args)..., and
value-initializes p[.](#out.ptr.t-6.sentence-1)
Then, equivalent to:
- [(6.1)](#out.ptr.t-6.1)
s.reset(); if the expression s.reset() is well-formed;
- [(6.2)](#out.ptr.t-6.2)
otherwise,s = Smart(); if is_constructible_v<Smart> is true;
- [(6.3)](#out.ptr.t-6.3)
otherwise, the program is ill-formed[.](#out.ptr.t-6.sentence-2)
[7](#out.ptr.t-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5533)
[*Note [2](#out.ptr.t-note-2)*:
The constructor is not noexcept to allow for a variety of non-terminating and safe implementation strategies[.](#out.ptr.t-7.sentence-1)
For example, an implementation can allocate
a shared_ptr's internal node in the constructor and
let implementation-defined exceptions escape safely[.](#out.ptr.t-7.sentence-2)
The destructor can then move the allocated control block in directly and
avoid any other exceptions[.](#out.ptr.t-7.sentence-3)
— *end note*]
[🔗](#lib:out_ptr_t,destructor)
`constexpr ~out_ptr_t();
`
[8](#out.ptr.t-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5551)
Let SP be*POINTER_OF_OR*(Smart, Pointer) ([[memory.general]](memory.general "20.2.1General"))[.](#out.ptr.t-8.sentence-1)
[9](#out.ptr.t-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5555)
*Effects*: Equivalent to:
- [(9.1)](#out.ptr.t-9.1)
if (p) { apply([&](auto&&... args) { s.reset(static_cast<SP>(p), std::forward<Args>(args)...); }, std::move(a));} if the expressions.reset(static_cast<SP>(p), std::forward<Args>(args)...) is well-formed;
- [(9.2)](#out.ptr.t-9.2)
otherwise,if (p) { apply([&](auto&&... args) { s = Smart(static_cast<SP>(p), std::forward<Args>(args)...); }, std::move(a));} if is_constructible_v<Smart, SP, Args...> is true;
- [(9.3)](#out.ptr.t-9.3)
otherwise, the program is ill-formed[.](#out.ptr.t-9.sentence-1)
[🔗](#out.ptr.t-itemdecl:3)
`constexpr operator Pointer*() const noexcept;
`
[10](#out.ptr.t-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5590)
*Preconditions*: operator void**() has not been called on *this[.](#out.ptr.t-10.sentence-1)
[11](#out.ptr.t-11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5594)
*Returns*: addressof(const_cast<Pointer&>(p))[.](#out.ptr.t-11.sentence-1)
[🔗](#out.ptr.t-itemdecl:4)
`operator void**() const noexcept;
`
[12](#out.ptr.t-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5604)
*Constraints*: is_same_v<Pointer, void*> is false[.](#out.ptr.t-12.sentence-1)
[13](#out.ptr.t-13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5608)
*Mandates*: is_pointer_v<Pointer> is true[.](#out.ptr.t-13.sentence-1)
[14](#out.ptr.t-14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5612)
*Preconditions*: operator Pointer*() has not been called on *this[.](#out.ptr.t-14.sentence-1)
[15](#out.ptr.t-15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5616)
*Returns*: A pointer value v such that:
- [(15.1)](#out.ptr.t-15.1)
the initial value *v is equivalent to static_cast<void*>(p) and
- [(15.2)](#out.ptr.t-15.2)
any modification of *v that is not followed by a subsequent modification of *this affects the value of p during the destruction of *this,
such that static_cast<void*>(p) == *v[.](#out.ptr.t-15.sentence-1)
[16](#out.ptr.t-16)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5629)
*Remarks*: Accessing *v outside the lifetime of *this has undefined behavior[.](#out.ptr.t-16.sentence-1)
[17](#out.ptr.t-17)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5634)
[*Note [3](#out.ptr.t-note-3)*:
reinterpret_cast<void**>(static_cast<Pointer*>(*this)) can be a viable implementation strategy for some implementations[.](#out.ptr.t-17.sentence-1)
— *end note*]
#### [20.3.4.2](#out.ptr) Function template out_ptr [[out.ptr]](out.ptr)
[🔗](#lib:out_ptr)
`template<class Pointer = void, class Smart, class... Args>
constexpr auto out_ptr(Smart& s, Args&&... args);
`
[1](#out.ptr-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5650)
Let P be Pointer if is_void_v<Pointer> is false,
otherwise *POINTER_OF*(Smart)[.](#out.ptr-1.sentence-1)
[2](#out.ptr-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5655)
*Returns*: out_ptr_t<Smart, P, Args&&...>(s, std::forward<Args>(args)...)[.](#out.ptr-2.sentence-1)
#### [20.3.4.3](#inout.ptr.t) Class template inout_ptr_t [[inout.ptr.t]](inout.ptr.t)
[1](#inout.ptr.t-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5662)
inout_ptr_t is a class template used to adapt types
such as smart pointers ([[smartptr]](smartptr "20.3Smart pointers"))
for functions that use output pointer parameters
whose dereferenced values may first be deleted
before being set to another allocated value[.](#inout.ptr.t-1.sentence-1)
[2](#inout.ptr.t-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5669)
[*Example [1](#inout.ptr.t-example-1)*: #include <memory>struct star_fish* star_fish_alloc();int star_fish_populate(struct star_fish** ps, const char* description);
struct star_fish_deleter {void operator() (struct star_fish* c) const noexcept;};
using star_fish_ptr = std::unique_ptr<star_fish, star_fish_deleter>;
int main(int, char*[]) { star_fish_ptr peach(star_fish_alloc()); // ...// used, need to re-makeint err = star_fish_populate(std::inout_ptr(peach), "caring clown-fish liker"); return err;}
A unique_ptr can be used with inout_ptr to be passed into an output pointer-style function[.](#inout.ptr.t-2.sentence-1)
The original value will be properly deleted
according to the function it is used with and
a new value reset in its place[.](#inout.ptr.t-2.sentence-2)
— *end example*]
[🔗](#lib:inout_ptr_t)
namespace std {template<class Smart, class Pointer, class... Args>class inout_ptr_t {public:constexpr explicit inout_ptr_t(Smart&, Args...);
inout_ptr_t(const inout_ptr_t&) = delete; constexpr ~inout_ptr_t(); constexpr operator Pointer*() const noexcept; operator void**() const noexcept; private: Smart& s; // *exposition only* tuple<Args...> a; // *exposition only* Pointer p; // *exposition only*};}
[3](#inout.ptr.t-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5720)
Pointer shall meet the [*Cpp17NullablePointer*](nullablepointer.requirements#:Cpp17NullablePointer "16.4.4.4Cpp17NullablePointer requirements[nullablepointer.requirements]") requirements[.](#inout.ptr.t-3.sentence-1)
If Smart is a specialization of shared_ptr,
the program is ill-formed[.](#inout.ptr.t-3.sentence-2)
[*Note [1](#inout.ptr.t-note-1)*:
It is impossible to properly acquire unique ownership of the managed resource
from a shared_ptr given its shared ownership model[.](#inout.ptr.t-3.sentence-3)
— *end note*]
[4](#inout.ptr.t-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5729)
Program-defined specializations of inout_ptr_t that depend on at least one program-defined type
need not meet the requirements for the primary template[.](#inout.ptr.t-4.sentence-1)
[5](#inout.ptr.t-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5734)
Evaluations of the conversion functions on the same object
may conflict ([[intro.races]](intro.races "6.10.2.2Data races"))[.](#inout.ptr.t-5.sentence-1)
[🔗](#lib:inout_ptr_t,constructor)
`constexpr explicit inout_ptr_t(Smart& smart, Args... args);
`
[6](#inout.ptr.t-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5744)
*Effects*: Initializes s with smart,a with std::forward<Args>(args)..., andp to either
- [(6.1)](#inout.ptr.t-6.1)
smart if is_pointer_v<Smart> is true,
- [(6.2)](#inout.ptr.t-6.2)
otherwise, smart.get()[.](#inout.ptr.t-6.sentence-1)
[7](#inout.ptr.t-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5754)
*Remarks*: An implementation can call s.release()[.](#inout.ptr.t-7.sentence-1)
[8](#inout.ptr.t-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5758)
[*Note [2](#inout.ptr.t-note-2)*:
The constructor is not noexcept to allow for a variety of non-terminating and safe implementation strategies[.](#inout.ptr.t-8.sentence-1)
For example, an intrusive pointer implementation with a control block
can allocate in the constructor and safely fail with an exception[.](#inout.ptr.t-8.sentence-2)
— *end note*]
[🔗](#lib:inout_ptr_t,destructor)
`constexpr ~inout_ptr_t();
`
[9](#inout.ptr.t-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5773)
Let SP be*POINTER_OF_OR*(Smart, Pointer) ([[memory.general]](memory.general "20.2.1General"))[.](#inout.ptr.t-9.sentence-1)
[10](#inout.ptr.t-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5777)
Let *release-statement* be s.release(); if an implementation does not call s.release() in the constructor[.](#inout.ptr.t-10.sentence-1)
Otherwise, it is empty[.](#inout.ptr.t-10.sentence-2)
[11](#inout.ptr.t-11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5782)
*Effects*: Equivalent to:
- [(11.1)](#inout.ptr.t-11.1)
apply([&](auto&&... args) { s = Smart(static_cast<SP>(p), std::forward<Args>(args)...); }, std::move(a)); if is_pointer_v<Smart> is true;
- [(11.2)](#inout.ptr.t-11.2)
otherwise,*release-statement*;if (p) { apply([&](auto&&... args) { s.reset(static_cast<SP>(p), std::forward<Args>(args)...); }, std::move(a));} if the expressions.reset(static_cast<SP>(p), std::forward<Args>(args)...) is well-
formed;
- [(11.3)](#inout.ptr.t-11.3)
otherwise,*release-statement*;if (p) { apply([&](auto&&... args) { s = Smart(static_cast<SP>(p), std::forward<Args>(args)...); }, std::move(a));} if is_constructible_v<Smart, SP, Args...> is true;
- [(11.4)](#inout.ptr.t-11.4)
otherwise, the program is ill-formed[.](#inout.ptr.t-11.sentence-1)
[🔗](#inout.ptr.t-itemdecl:3)
`constexpr operator Pointer*() const noexcept;
`
[12](#inout.ptr.t-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5826)
*Preconditions*: operator void**() has not been called on *this[.](#inout.ptr.t-12.sentence-1)
[13](#inout.ptr.t-13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5830)
*Returns*: addressof(const_cast<Pointer&>(p))[.](#inout.ptr.t-13.sentence-1)
[🔗](#inout.ptr.t-itemdecl:4)
`operator void**() const noexcept;
`
[14](#inout.ptr.t-14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5840)
*Constraints*: is_same_v<Pointer, void*> is false[.](#inout.ptr.t-14.sentence-1)
[15](#inout.ptr.t-15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5844)
*Mandates*: is_pointer_v<Pointer> is true[.](#inout.ptr.t-15.sentence-1)
[16](#inout.ptr.t-16)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5848)
*Preconditions*: operator Pointer*() has not been called on *this[.](#inout.ptr.t-16.sentence-1)
[17](#inout.ptr.t-17)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5852)
*Returns*: A pointer value v such that:
- [(17.1)](#inout.ptr.t-17.1)
the initial value *v is equivalent to static_cast<void*>(p) and
- [(17.2)](#inout.ptr.t-17.2)
any modification of *v that is not followed by subsequent modification of *this affects the value of p during the destruction of *this,
such that static_cast<void*>(p) == *v[.](#inout.ptr.t-17.sentence-1)
[18](#inout.ptr.t-18)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5865)
*Remarks*: Accessing *v outside the lifetime of *this has undefined behavior[.](#inout.ptr.t-18.sentence-1)
[19](#inout.ptr.t-19)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5870)
[*Note [3](#inout.ptr.t-note-3)*:
reinterpret_cast<void**>(static_cast<Pointer*>(*this)) can be a viable implementation strategy for some implementations[.](#inout.ptr.t-19.sentence-1)
— *end note*]
#### [20.3.4.4](#inout.ptr) Function template inout_ptr [[inout.ptr]](inout.ptr)
[🔗](#lib:inout_ptr)
`template<class Pointer = void, class Smart, class... Args>
constexpr auto inout_ptr(Smart& s, Args&&... args);
`
[1](#inout.ptr-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5886)
Let P be Pointer if is_void_v<Pointer> is false,
otherwise *POINTER_OF*(Smart)[.](#inout.ptr-1.sentence-1)
[2](#inout.ptr-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/memory.tex#L5890)
*Returns*: inout_ptr_t<Smart, P, Args&&...>(s, std::forward<Args>(args)...)[.](#inout.ptr-2.sentence-1)