Hi Kevin -
Your explanation makes sense, and I apologize for the confusion.
The upcxx::dist_object<T> comprising a distributed object need not involve any storage in the shared memory segment. In fact, in many common use cases for dist_object<T> these objects live on the program stack, meaning both the dist_object and embedded T object reside entirely in private memory (and attempting to call upcxx::to_global_pointer() on the address of such an object should yield an assertion failure in debug codemode). Another common use case is dist_object<global_ptr<T>>, where the dist_object and global pointer themselves usually live in private memory, but the contained global pointer references an object in the shared heap. It might be helpful to think of distributed objects as a collective abstraction which is mostly useful for RPC and dist_object::fetch(), and is mostly orthogonal to object storage in the shared segment, which is mostly used for RMA operations (rput/rget/copy) or local_team bypass.
FWIW we also provide a upcxx::try_global_pointer(lp) function which operates analogously to upcxx::to_global_pointer(lp), except that it guarantees that passing an lp referencing private memory will yield a null global pointer, even in opt codemode, rather than undefined behavior. So that variant might be a good choice anywhere you're not 100% certain the input points into the shared segment.
Cheers,
-D