Home > Software design >  is there any safe way to derive the offset of a T inside a std::optional<T>?
is there any safe way to derive the offset of a T inside a std::optional<T>?

Time:01-19

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);
        };
};

CodePudding user response:

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.

CodePudding user response:

If the optional is not empty then it's safe to get its address.

Take a look at this simple example:

#include <iostream>
#include <optional>


int main( )
{
    std::optional<int> num1 { 5 };
    std::optional<int> num2 { std::nullopt };

    std::cout << &( num1.value( ) ) << '\n';
    std::cout << &num1 << "\n\n";

    std::cout << &( num2.value( ) ) << '\n'; // this statement will throw
    std::cout << &num2 << '\n'; // not this
}

Output:

0xeceafffc20
0xeceafffc20

terminate called after throwing an instance of 'std::bad_optional_access'
  what():  bad optional access

As you can see, getting the address of the second std::optional throws an exception.

However, as mentioned by @user4581301, the standard does not guarantee that the address of the T will be identical to the address of the std::optional object.

  •  Tags:  
  • Related