7

I would like to implement a Scala-like Option / Haskell-like Maybe class in C++. For efficiency reasons, I do not want to use dynamically allocated memory, nor do I want to use polymorphism. In addition, I do not want any object of the embedded type to be created if the Option is None.

Can anybody tell me whether the following approach may cause problems? I have to statically allocate memory for the embedded object within my Option class, but I cannot define a member field of the embedded type, since this would be initialized on creation of the Option object even if the Option is None.

template <typename T>
class Option {
private:
    uint8_t _storage [sizeof (T)];
    T * _embedded;
public:
    Option () : _embedded (nullptr) {
    }

    Option (const T & obj) : _embedded (new (_storage) T (obj)) {
    }

    Option (const Option<T> & other)
    : _embedded (
        other->_embedded ? new (_storage) T (other->_embedded) : nullptr
    ) {
    }

    // ...

    ~Option () {
        if (_embedded) _embedded->~T ();
    }
};
JohnB
  • 13,315
  • 4
  • 38
  • 65
  • 2
    You could check how [Boost.Optional](http://www.boost.org/doc/libs/1_51_0/libs/optional/doc/html/index.html) is implemented. – kennytm Oct 21 '12 at 08:57
  • Thanks for the hint. I should have known Boost has it. – JohnB Oct 21 '12 at 10:46
  • 1
    It's quite a clever representation, actually. You would need to deal with **assignment** too, but otherwise I really like the idea of directly storing a pointer rather than just a boolean. Makes things easier afterward for sure. – Matthieu M. Oct 21 '12 at 13:36
  • You are about half a step away from implementing a 'Variant' type class here. – John Dibling Oct 21 '12 at 15:49

2 Answers2

3

I don't think that the array is required to be aligned the same way the object class may require. In practice I wouldn't expect any problems unless the type has funny alignment requirements.

With C++ 2011 you could use a union to hold the actual representation, although you'd still need to manage the held object’s life-time. There is a boost::optional<T> and a proposal to add a similar type to the next revision of the standard.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • You could probably use boost::aligned_storage in order to get correct alignment of the buffer. – mauve Oct 21 '12 at 09:24
  • Returning not aligned memory from placement new is UB according to answers to this question: http://stackoverflow.com/questions/11781724/do-i-really-have-to-worry-about-alignment-when-using-placement-new-operator – PiotrNycz Oct 21 '12 at 09:49
  • Actually, in C++11 I would expect `std::aligned_storage` to be used. It was created specifically to request raw storage of a precise size *and alignment*. – Matthieu M. Oct 21 '12 at 13:34
  • For those interested in the result of the proposal: C++17 introduced [std::optional](http://en.cppreference.com/w/cpp/utility/optional). – Wolf Nov 14 '17 at 15:34
1

For me this looks fine, except for:

uint8_t _storage [sizeof(T)/sizeof(uint8_t)];

Option (const Option & other)
    : _embedded (other->_embedded ? new (_storage)T(other->_embedded) : nullptr)
{
}
Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
  • What about `char _storage [sizeof(T)];` since [`char` is always 1](http://en.cppreference.com/w/cpp/language/sizeof) in size? – Wolf Nov 14 '17 at 15:55