Consider the following function, which returns the offset in bytes between the I
-th element of a tuple and the tuple address:
template <std::size_t I, class... Ts>
requires ( ( not std::is_reference_v<Ts> ) && ... )
std::intptr_t tuple_elem_offset(const std::tuple<Ts...>& tuple)
{
const auto element_address = reinterpret_cast<std::intptr_t>(&std::get<I>(tuple));
const auto tuple_address = reinterpret_cast<std::intptr_t>(&tuple);
return element_address - tuple_address;
}
Is it guaranteed that the value returned is always in the range [0, sizeof(std::tuple<Ts...>))
?
I understand tuples are typically implemented using multiple inheritance, and that this would imply the nested-within property for all non-reference objects held by the tuple. My question is: is it enforced by the language?
There is no guarantee that the elements are nested in the tuple
object, although I have never seen an implementation that doesn't behave that way.
Regardless of the answer to your title question however, the behavior of the shown program is implementation-defined.
There is no guarantee given in the standard that reinterpret_cast<std::intptr_t>
on a pointer will return a value related to the address that the pointer represents or that it can be used to calculate offsets between objects and their subobjects. But again, in practice that is the only behavior I have seen in implementations.
Being able to calculate offsets like this does not imply that it can be used to get access to the tuple elements though from some other original pointer. So it isn't clear how knowing these offsets would be helpful.
Also, it isn't even guaranteed that the memory occupied by an object such as std::tuple<Ts...>
is contiguous, which you seem to imply otherwise with [0, sizeof(std::tuple<Ts...>))
. That is only guaranteed for standard-layout types and trivially-copyable types. (Although this in practice only matters for virtual inheritance that should in turn be irrelevant for a normal std::tuple
implementation.)