Standard Library Idioms
CPO (Customization Point Object)
A function object in the standard library that discovers user-defined implementations via ADL:
// std::ranges::begin is a CPO
// when calling ranges::begin(v):
// 1. if v has a member begin() → call v.begin()
// 2. otherwise find begin(v) via ADL
// 3. otherwise fall back to the default implementation
namespace MyLib {
struct Container { ... };
auto begin(Container& c) { return c.data(); } // discovered via ADL
}
MyLib::Container c;
auto it = std::ranges::begin(c); // found MyLib::begin via ADLNiebloid
A form of CPO implementation — a global function object that works through the ADL mechanism but is not itself a function:
// ranges::begin is a niebloid (named after Eric Niebler)
namespace std::ranges {
inline constexpr __begin_fn begin{}; // global constexpr function object
}Tag Invoke (C++20 Ranges Customization Mechanism)
// recommended way for user types to customize ranges::begin
template<typename T>
auto tag_invoke(ranges::begin_t, MyContainer<T>& c) {
return my_iterator<T>(c.data());
}Range / View / Pipe Operator
- Range: Anything that has
begin()andend() - View: A lightweight range adapter — O(1) copy, O(1) default construction, non-owning
- Pipe Operator: The
|operator passes a range to a view adapter
See the range-v3 pipe operator chapter for details.
Sentinel
end() does not have to be an iterator — it can be a "sentinel," any type comparable with an iterator:
// sentinel for null-terminated strings
struct null_sentinel {
bool operator==(const char* p) const { return *p == '\0'; }
};
// usage
const char* s = "hello";
auto range = ranges::subrange(s, null_sentinel{});PMR (Polymorphic Memory Resource, C++17)
A runtime-polymorphic memory allocator — dispatched via virtual functions rather than template parameters:
std::pmr::monotonic_buffer_resource pool(buf, sizeof(buf));
std::pmr::vector<int> v(&pool); // uses pool for memory allocation
v.push_back(42); // allocates from pool, not from global newAllocator Model
The standard library allocator is type-bound — vector<int, MyAlloc> and vector<double, MyAlloc> are different types. This makes it difficult to switch allocation strategies at runtime. PMR and EASTL's non-template allocators are two solutions to this problem.
Smart Pointer Idioms
// unique_ptr: exclusive ownership, zero overhead (8 bytes)
auto p = std::make_unique<Widget>(args...);
// shared_ptr: shared ownership, reference counting (16 bytes)
auto p = std::make_shared<Widget>(args...); // single allocation
// weak_ptr: does not own the object, can check if the object is still alive
std::weak_ptr<Widget> wp = sp;
if (auto locked = wp.lock()) { /* object is still alive */ }RAII Handle
The standard library makes extensive use of RAII to wrap operating system resources:
std::lock_guard<std::mutex> lk(mtx); // mutex lock
std::unique_ptr<File> fp(open(...)); // file handle
std::jthread worker(do_work); // thread