
class string { string() = default; string & operator=(string && other) noexcept { delete m_data; m_data = other.m_data; other.m_data = nullptr; m_capacity = other.m_capacity; other.m_capacity = 0; m_size = other.m_size; other.m_size = 0; return *this; } private: friend optional<string>; char * m_data = nullptr; size_t m_capacity = 0; size_t m_size = 0; }; template<> class optional_storage<string> { optional_storage() noexcept { reset(); } template<typename... Args> explicit optional_storage(in_place_t, Args && ... args): m_value(std::forward<Args>(args)...) { } template<typename... Args> void emplace(Args && ... args) { m_value = string(std::forward<Args>(args)...); } void reset() noexcept { delete m_value.m_data; m_value.m_data = nullptr; m_value.m_size = uninitialized_size(); } bool is_initialized() const noexcept { return m_value.m_size == uninitialized_size(); } auto && value() const & noexcept { return m_value; } auto && value() & noexcept { return m_value; } auto && value() && noexcept { return std::move(m_value); } private: static constexpr auto uninitialized_size() noexcept { return std::numeric_limits<std::size_t>::max(); } string m_value; }; Other than looking at its size, I do not believe that the user can tell whether we have done this or have used the default optional. It is undefined behavior to call operator*() on an uninitialized optional, and every other way of getting at the value first checks is_initialized(), so no one can ever have a reference to a string that has been set to the uninitialized state. I included the implementation of the move assignment operator to show that it does not change at all with this optimization. It can just blindly delete what it used to have and do assignments as usual. emplace can call the move assignment operator after constructing a temporary, because that is identical to destruct + construct. I would expect the optimizer would compile that down to the same code, in fact, as an explicit destructor and a placement new. reset has to set the pointer to nullptr when uninitializing to make it safe to blindly call delete again.