CodeNebula
C++20stladvanced

std::to_address (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

Obtains the raw pointer from a pointer-like type (fancy pointer, smart pointer, or raw pointer) without dereferencing it. Works even for pointers to incomplete types or types that overload `operator*`.

Real-World Scenario

Pass the underlying address from a custom allocator's fancy pointer to a C API that requires a raw pointer.

Quick Snippet

snippet.cpp
#include <memory>
#include <vector>

std::vector<int> data = {1, 2, 3};
auto ptr = std::to_address(data.begin());  // int* to first element

auto sp = std::make_shared<int>(42);
int* raw = std::to_address(sp);  // raw pointer, no ref-count change

Full Example

to_address.cpp
// CodeNebula - C++20 Example: std::to_address
// Compile: g++ -std=c++20 -Wall -Wextra -o example to_address.cpp

#include <iostream>
#include <memory>
#include <vector>

// A simple fancy pointer type that wraps a raw pointer
template <typename T>
class FancyPtr {
    T* ptr_;
public:
    using element_type = T;

    explicit FancyPtr(T* p = nullptr) : ptr_(p) {}
    T& operator*() const { return *ptr_; }
    T* operator->() const { return ptr_; }

    // to_address uses pointer_traits<FancyPtr>::to_address if available,
    // otherwise falls back to operator->()
    friend T* to_address_helper(const FancyPtr& fp) { return fp.ptr_; }
};

// Specialize pointer_traits for FancyPtr
template <typename T>
struct std::pointer_traits<FancyPtr<T>> {
    using pointer = FancyPtr<T>;
    using element_type = T;
    using difference_type = std::ptrdiff_t;

    static T* to_address(const FancyPtr<T>& p) noexcept {
        return &(*p);
    }
};

int main() {
    std::cout << "=== std::to_address with raw pointers ===\n";
    int x = 42;
    int* raw = &x;
    // to_address on a raw pointer returns the pointer itself
    std::cout << "to_address(raw) == raw: " << (std::to_address(raw) == raw) << "\n";
    std::cout << "*to_address(raw) = " << *std::to_address(raw) << "\n";

    std::cout << "\n=== std::to_address with smart pointers ===\n";
    auto sp = std::make_shared<int>(100);
    int* from_shared = std::to_address(sp);
    std::cout << "shared_ptr value via to_address: " << *from_shared << "\n";
    std::cout << "to_address(sp) == sp.get(): " << (from_shared == sp.get()) << "\n";

    auto up = std::make_unique<double>(3.14);
    double* from_unique = std::to_address(up);
    std::cout << "unique_ptr value via to_address: " << *from_unique << "\n";

    std::cout << "\n=== std::to_address with fancy pointer ===\n";
    double val = 2.718;
    FancyPtr<double> fancy(&val);
    double* from_fancy = std::to_address(fancy);
    std::cout << "FancyPtr value via to_address: " << *from_fancy << "\n";
    std::cout << "Addresses match: " << (from_fancy == &val) << "\n";

    std::cout << "\n=== Practical use: passing iterator data to C API ===\n";
    std::vector<int> vec = {10, 20, 30, 40, 50};
    // Safely get the raw pointer from a vector iterator
    int* data_ptr = std::to_address(vec.begin());
    std::cout << "First 3 elements via raw pointer: ";
    for (int i = 0; i < 3; ++i)
        std::cout << data_ptr[i] << " ";
    std::cout << "\n";

    return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -o example to_address.cpp