0

Given a std::optional<T> is it possible to derive the address of the std::optional<T> from the address of the T?

possible implementation:

template<typename T>
struct myoptional
{       static std::size_t getOffset(void)
        {       static const myoptional<T> s(std::inplace());
                return reinterpret_cast<const char*>(&s.value())
                       - reinterpret_cast<const char*>(&s);
        };
};
digito_evo
  • 3,216
  • 2
  • 14
  • 42
Frank Puck
  • 467
  • 1
  • 11

1 Answers1

2

It is not specified where exactly in its storage std::optional stores the object. If you assume that the location of the object is always the same for the same type T, you can maybe (see below) use the shown method to obtain an offset through the object representation.

However, even if you knew the offset of the object, you would still not be able to obtain a usable pointer to the std::optional<T> from a pointer to the T object. You would need to std::launder the resulting pointer to make it usable, which will only be allowed if sizeof(std::optional<T>) <= sizeof(T), which seems unlikely/impossible. (Otherwise it would make previously unreachable bytes reachable in the meaning of the term reachable as in the preconditions for std::launder, see https://en.cppreference.com/w/cpp/utility/launder)


Whether or not the use of the reinterpret_cast and the pointer arithmetic is allowed by the standard is not clear at the moment. The standard has some unresolved defects in that regard.

What you can do alternatively though and (as far as I can tell) shouldn't have undefined behavior is to use a union of a std::optional<T> with a unsigned char[sizeof(std::optional<T>)] and instead of using a pointer difference, you can then use addition and pointer comparison in a loop to find the offset in the character array matching the address of the T value. See e.g this answer.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • @FrançoisAndrieux If `sizeof(std::optional) > sizeof(T)` then the pointer to the `T` object will not be able to reach some bytes in the storage occupied by the `std::optional` object. Therefore trying to get from a `T*` to a `std::optional*` will be impossible. (This is the direction OP is asking about in the question body.) – user17732522 Jan 18 '22 at 22:57
  • The question of pointer arithmetic on object representation is a current language defect. The intention is that it should be allowed but the wording does not actually support it yet. – François Andrieux Jan 18 '22 at 23:11
  • @FrançoisAndrieux Well, it can't be smaller because it is specified to provide storage inside itself for the `T` object. – user17732522 Jan 18 '22 at 23:11
  • @FrançoisAndrieux I hope I addressed your comments in the answer. Regarding the object representation, it is not so clear to me whether it is supposed to work in this way. The problem is that if the shown casts were allowed and resulted in pointers to elements of the same array (the object representation of the `std::optional` object), which is necessary for the pointer arithmetic to be allowed, then this cast would effectively circumvent the `std::launder` reachability requirement. You would always be able to just cast to `char*`/`unsigned char*` first. – user17732522 Jan 18 '22 at 23:19