2012-10-21 17 views
5

Chciałbym zaimplementować opcję typu Scala-like/Haskell-Like w C++. Ze względów wydajnościowych nie chcę używać dynamicznie przydzielanej pamięci, ani nie chcę używać polimorfizmu. Ponadto nie chcę, aby tworzony był obiekt typu osadzonego, jeśli opcja jest Brak.Opcja/być może klasa dla C++

Czy ktoś może mi powiedzieć, czy poniższe podejście może powodować problemy? Muszę statycznie przydzielić pamięć dla osadzonego obiektu w mojej klasie Option, ale nie mogę zdefiniować pola elementu typu osadzonego, ponieważ byłoby to zainicjowane podczas tworzenia obiektu Option, nawet jeśli opcja jest Brak.

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

Można sprawdzić, w jaki sposób zaimplementowano [Boost.Optional] (http://www.boost.org/doc/libs/1_51_0/libs/optional/doc/html/index.html). – kennytm

+0

Dzięki za podpowiedź. Powinienem był wiedzieć, że Boost to ma. – JohnB

+1

To właściwie sprytna reprezentacja. Będziesz musiał również zająć się ** zadaniem **, ale poza tym bardzo podoba mi się pomysł bezpośredniego zapisywania wskaźnika, a nie tylko wartości logicznej. Na pewno ułatwia to sprawy. –

Odpowiedz

3

Nie sądzę, że tablica jest wymagane, aby zostać dostosowane w taki sam sposób klasa obiektu może wymagać. W praktyce nie spodziewałbym się żadnych problemów, chyba że typ ma zabawne wymagania dotyczące wyrównania.

W C++ 2011 można użyć modelu union do przechowywania rzeczywistej reprezentacji, chociaż nadal trzeba zarządzać czasem życia obiektu wstrzymanego. Istnieje boost::optional<T> i proposal, aby dodać podobny typ do następnej wersji standardu.

+0

Prawdopodobnie możesz użyć boost :: aligned_storage, aby uzyskać prawidłowe wyrównanie bufora. – mauve

+0

Zwracanie nie wyrównanej pamięci z nowego miejsca docelowego to UB zgodnie z odpowiedziami na to pytanie: http://stackoverflow.com/questions/11781724/do-i-altern-have-to-worry-about-alignment-when-using-placement -new-operator – PiotrNycz

+0

Właściwie w C++ 11 oczekiwałbym użycia 'std :: aligned_storage'. Został stworzony specjalnie w celu żądania surowego przechowywania dokładnego rozmiaru * i wyrównania *. –

1

Dla mnie to wygląda dobrze, z wyjątkiem:

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

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

Co z 'char _storage [sizeof (T)];' ponieważ ['char' jest zawsze 1] (http://en.cppreference.com/w/cpp/language/sizeof) w rozmiarze? – Wolf