1. Problem It Solves
C APIs often return ownership through T**, which does not directly match a C++ smart pointer. std::out_ptr and std::inout_ptr adapt that output parameter while restoring RAII ownership afterward. The goal is to make the relevant rule visible in code instead of relying on an assumption about what “C++23 support” means.
2. Prerequisites
Day 3: ownership transfer.
Day 6: RAII and resource lifetime.
3. Core Idea
The adapter temporarily opens a controlled raw-pointer doorway. out_ptr expects fresh output; inout_ptr also exposes an existing pointer that the C function may replace. Read the syntax from left to right, identify the value, object, or range being transformed, and then check its resulting type, lifetime, or ownership. Standardization and implementation are separate: a C++23 mode may still lack one library component, so a feature-test macro is part of responsible portable use.
4. Minimal Syntax
legacy_create(std::out_ptr(owner));5. How It Works
The sample builds the smallest expression or object needed for
std::out_ptrandstd::inout_ptr.It lets tiny C-style create and replace functions populate a
std::unique_ptrwhen supported. The compiler and library apply the relevant rule before the program observes the result.The program prints or checks the value owned by the smart pointer after adaptation, making the important behavior easy to verify.
6. Common Mistakes
Using
inout_ptrwith an API that neither releases nor replaces the old pointer can leak or double-delete; a custom deleter must match the C allocation function.Enabling C++23 mode without checking the relevant feature macro may select code that the installed compiler or standard library does not implement yet.
7. When to Use It
Use it when the task involves interfacing an owning smart pointer with a well-documented legacy C output parameter.
Avoid it when the task involves new C++ APIs, which should return RAII owners directly rather than expose
T**.
8. Simple Example
A legacy image loader writes a newly allocated handle through Handle**, while C++ stores the result in a unique owner. The downloadable program keeps the data fixed so the output can be compared without entering input.
Complete sample code
Source file
cpp23/32_out_ptr_inout_ptr/main.cpp
#include <iostream>
#include <memory>
#include <version>
#if defined(__cpp_lib_out_ptr)
void create_value(int** output) {
*output = new int(7);
}
void replace_value(int** output) {
delete *output;
*output = new int(9);
}
#endif
int main() {
#if defined(__cpp_lib_out_ptr)
std::unique_ptr<int> owner;
create_value(std::out_ptr(owner));
replace_value(std::inout_ptr(owner));
std::cout << *owner << '\n';
#else
std::cout << "out_ptr and inout_ptr unavailable\n";
#endif
}
9. Key Takeaways
Separate the language rule from compiler and library availability.
Keep lifetime, ownership, and deduced types visible when they affect correctness.
Prefer the smallest syntax that communicates the intent.
Guard facilities that are not yet uniformly implemented.
10. Self-Check Questions
Easy — What is the smallest syntax in Section 4, and what main job does it perform?
Medium — Read the sample program: which value, type, or branch is observed, and why?
Hard — Why is
out_ptrappropriate for an empty owner butinout_ptrrequires a precise contract for what the C function does with the old pointer?