The hidden detail behind std::addressof in C++
In c++, even something like &obj is not always what it seems.
Normally, when we write:
struct Resource {
int data = 42;
};
int main() {
Resource r;
auto h = &r;
std::cout << h << '\n';
}
we expect &r to give us the memory address of r, and in most cases, it does.
But C++ allows something interesting, operators can be overloaded.
When & is Overloaded:
Let’s modify the example:
#include <iostream>
struct Handle {
void* ptr;
};
struct Resource {
int data = 42;
Handle operator&() {
std::cout << "Returning handle instead of raw address\n";
return Handle{this};
}
};
int main() {
Resource r;
auto h = &r;
std::cout << "Handle points to: " << h.ptr << "\n";
}
Now something unexpected happens.
Instead of returning the actual memory address of r, the & operator is overloaded to return a custom Handle object.
So the question becomes:
How do we still get the real address of the object?
This is exactly what std::addressof is designed for.
When the & operator is overloaded, we can no longer rely on it to give the true address. Instead, std::addressof bypasses the overloaded operator and retrieves the actual underlying memory address.
But how does std::addressof works ?
A simplified version looks like this:
template<class T>
T* addressof(T& arg) noexcept
{
return reinterpret_cast<T*>(
&const_cast<char&>(
reinterpret_cast<const volatile char&>(arg)
)
);
}
It looks scary at first, but once you see what it’s doing, it’s actually a very deliberate trick.
It does 3 things:
View the object as raw bytes
The object is first reinterpreted as a sequence of bytes:reinterpret_cast<const volatile char&>(arg)We use
charbecause it allows safe access to the raw memory representation of any object since it is one byte in size.
This is a common technique in low level C++ implementations like libc++. However, in modern C++,std::byteis often preferred because it makes the intent clearer and improves readability.Remove const volatile.
const_cast<char&>(...)Why?
This step removesconstandvolatilequalifiers so we can take a clean address of the underlying byte representation.Take the actual address.
&const_cast<char&>(...)Now we can safely take the address because
chardoes not have an overloadedoperator&.Cast back to the original type
reinterpret_cast<T*>(...)Finally, we convert the pointer from
char*back to the original typeT*.
Final Thoughts
This is a great example of how C++ gives you both:
high level abstractions (like operator overloading)
and low level control (like raw memory access)
std::addressof is just a safe way to get the real memory address, even if operator& has been overloaded.
