20 General utilities library [utilities]

20.11 Smart pointers [smartptr]

20.11.8 Class template out_­ptr_­t [out.ptr.t]

out_­ptr_­t is a class template used to adapt types such as smart pointers ([smartptr]) for functions that use output pointer parameters.
[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 valid return 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.
— end example]
namespace std { template<class Smart, class Pointer, class... Args> class out_ptr_t { public: explicit out_ptr_t(Smart&, Args...); out_ptr_t(const out_ptr_t&) = delete; ~out_ptr_t(); operator Pointer*() const noexcept; operator void**() const noexcept; private: Smart& s; // exposition only tuple<Args...> a; // exposition only Pointer p; // exposition only }; }
Pointer shall meet the Cpp17NullablePointer requirements.
If Smart is a specialization of shared_­ptr and sizeof...(Args) == 0, the program is ill-formed.
[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].
— end note]
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.
Evaluations of the conversion functions on the same object may conflict ([intro.races]).
explicit out_ptr_t(Smart& smart, Args... args);
Effects: Initializes s with smart, a with std​::​forward<Args>(args)..., and value-initializes p.
[Note 2:
The constructor is not noexcept to allow for a variety of non-terminating and safe implementation strategies.
For example, an implementation can allocate a shared_­ptr's internal node in the constructor and let implementation-defined exceptions escape safely.
The destructor can then move the allocated control block in directly and avoid any other exceptions.
— end note]
Let SP be POINTER_­OF_­OR(Smart, Pointer) ([memory.general]).
Effects: Equivalent to:
  • -- if (p) { apply([&](auto&&... args) { s.reset(static_cast<SP>(p), std::forward<Args>(args)...); }, std::move(a)); } if the expression s.reset(static_­cast<SP>(p), std​::​forward<Args>(args)...) is well-
  • 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;
  • otherwise, the program is ill-formed.
operator Pointer*() const noexcept;
Preconditions: operator void**() has not been called on *this.
Returns: addressof(const_­cast<Pointer&>(p)).
operator void**() const noexcept;
Constraints: is_­same_­v<Pointer, void*> is false.
Mandates: is_­pointer_­v<Pointer> is true.
Preconditions: operator Pointer*() has not been called on *this.
Returns: A pointer value v such that:
  • the initial value *v is equivalent to static_­cast<void*>(p) and
  • 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.
Remarks: Accessing *v outside the lifetime of *this has undefined behavior.
[Note 3:
reinterpret_­cast<void**>(static_­cast<Pointer*>(*this)) can be a viable implementation strategy.
— end note]