2016-11-04 6 views
3

Je suis tombé sur une implémentation de Optional<T> qui est basée sur la classe Optional.h de LLVM et n'arrivait pas à comprendre pourquoi elle est implémentée comme elle est.Pourquoi l'option <T> de LLVM est-elle implémentée de cette façon?

Pour être bref, je ne coller les pièces que je ne comprends pas:

template <typename T> 
class Optional 
{ 
private: 
    inline void* getstg() const { return const_cast<void*>(reinterpret_cast<const void*>(&_stg)); } 
    typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type storage_type; 
    storage_type _stg; 
    bool _hasValue; 

public: 

    Optional(const T &y) : _hasValue(true) 
    { 
     new (getstg()) T(y); 
    } 

    T* Get() { return reinterpret_cast<T*>(getstg()); } 
} 

Et la mise en œuvre la plus naïve que je pouvais penser:

template <typename T> 
class NaiveOptional 
{ 
private: 
    T* _value; 
    bool _hasValue; 

public: 
    NaiveOptional(const T &y) : _hasValue(true), _value(new T(y)) 
    { 
    } 

    T* Get() { return _value; } 
} 

Questions:

  1. Comment interpréter le storage_type? quelle était l'intention de l'auteur?
  2. Quelle est la sémantique de cette ligne: new (getstg()) T(y);?
  3. Pourquoi l'implémentation naïve ne fonctionne-t-elle pas (ou, quels sont les avantages de la classe Optional<T> sur NaiveOptional<T>)?
+2

1) pour le placement mémoire alignée T (voir [doc] (http://en.cppreference.com/w/cpp/types/aligned_storage)), 2) neuf, 3) pros de plus en option naïve: vous enregistrez une allocation dynamique. – Borgleader

+0

Aussi loin que le # 2 va: il s'appelle * placement new * et il dit fondamentalement 'new' pour construire l'objet à un endroit spécifique que vous avez déjà alloué. –

+0

P.S: Dans votre implémentation naïve, le booléen est redondant, vous pouvez simplement comparer à nullptr pour savoir si vous avez une valeur ou non. – Borgleader

Répondre

6

La réponse courte est "performance".

réponse plus long:

  1. storage_type fournit une région de mémoire qui est (a) suffisamment grande pour être compatibles avec le type T et (b) est aligné correctement pour l'accès à la mémoire de type T. Unaligned est plus lente. Voir aussi le doc.
  2. new (getstg()) T(y) est un emplacement nouveau. Il n'alloue pas de mémoire, mais construit un objet dans la région mémoire qui lui est transmise. Le doc (sur toutes les formes de new - recherchez le "placement nouveau").
  3. L'implémentation naïve fonctionne, mais elle est moins performante. Il utilise l'allocation de mémoire dynamique, qui peut souvent être un goulot d'étranglement. L'implémentation Optional<T> n'utilise pas l'allocation de mémoire dynamique (voir le point ci-dessus).
-1

Std :: optionnel est censé être renvoyé par les fonctions. Cela signifie que vous devrez stocker le contenu référencé par le pointeur quelque part. Cela va à l'encontre du but de cette classe

En outre, vous ne pouvez pas utiliser simplement T, car sinon vous devrez le construire d'une manière ou d'une autre. Facultatif permet au contenu d'être non initialisé, certains types ne peuvent pas être construits par défaut.

Pour rendre la classe plus flexible en termes de types pris en charge, un stockage adapté et correctement aligné est utilisé. Et seulement si optionnel est actif, le vrai type sera construit dessus

Ce que vous aviez en tête est probablement quelque chose comme std :: variant?