2012-10-21 13 views
5

Vorrei implementare un'opzione Scala-like/Haskell-like in C++. Per ragioni di efficienza, non voglio usare memoria allocata dinamicamente, né voglio usare il polimorfismo. Inoltre, non desidero creare alcun oggetto del tipo incorporato se l'opzione è Nessuno.Opzione/Forse classe per C++

Qualcuno può dirmi se il seguente approccio potrebbe causare problemi? Devo allocare staticamente memoria per l'oggetto incorporato all'interno della mia classe Option, ma non posso definire un campo membro del tipo embedded, poiché questo sarebbe inizializzato alla creazione dell'oggetto Option anche se l'opzione è 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(); 
    } 
}; 
+2

È possibile verificare come viene implementato [Boost.Optional] (http://www.boost.org/doc/libs/1_51_0/libs/optional/doc/html/index.html). – kennytm

+0

Grazie per il suggerimento. Avrei dovuto sapere che Boost ce l'ha. – JohnB

+1

In realtà è una rappresentazione abbastanza intelligente. Dovresti occuparti anche di ** assignment **, ma altrimenti mi piace molto l'idea di memorizzare direttamente un puntatore piuttosto che un semplice booleano. Rende le cose più facili in seguito. –

risposta

3

non credo che l'array è necessario per essere allineati allo stesso modo in cui la classe di oggetti può richiedere. In pratica non mi aspetterei nessun problema a meno che il tipo non abbia dei buffi requisiti di allineamento.

Con C++ 2011 è possibile utilizzare uno union per contenere la rappresentazione effettiva, sebbene sia ancora necessario gestire la durata dell'oggetto in oggetto. C'è un boost::optional<T> e un proposal per aggiungere un tipo simile alla prossima revisione dello standard.

+0

Probabilmente potresti usare boost :: aligned_storage per ottenere l'allineamento corretto del buffer. – mauve

+0

Il ritorno della memoria non allineata dal posizionamento nuovo è UB in base alle risposte a questa domanda: http://stackoverflow.com/questions/11781724/do-i-really-have-to-worry-about-alignment-when-using-placement -new-operator – PiotrNycz

+0

In realtà, in C++ 11 mi aspetterei di utilizzare 'std :: aligned_storage'. È stato creato appositamente per richiedere lo storage raw di dimensioni precise * e allineamento *. –

1

Per me questo sembra bene, tranne per:

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

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

Che dire di 'char _storage [sizeof (T)];' dato che ['char' è sempre 1] (http://en.cppreference.com/w/cpp/language/sizeof) nella dimensione? – Wolf