2011-10-09 4 views
2

J'écris un récipient en forme d'arbre, où chaque « nœud » a une liste avec des branches/sous-arbres, actuellement ma tête ressemble à:Déplacer la sémantique et la sémantique de référence

class _tree { 
public: 
    typedef _tree* tree_ptr; 
    typedef std::list<_tree> _subTreeTy; 

    explicit _tree(const _ValTy& v, const _NameTy& n); //create a new tree 
    _tree(const _ValTy& v, const _NameTy& n, tree_ptr _root); 
     //create a new tree and add it as branch to "_root". 

    ~_tree(); 

    void add_branch(const _tree& branch); //add by copy 
    void add_branch(_tree&& branch); //add by move 
private: 
    _subTreeTy subtrees; 
    _ValTy value; 
    _NameTy name; 
}; 


_tree::_tree(const _ValTy& v, const _NameTy& n, tree_ptr _root) 
    : root(_root), 
    value(v), 
    name(n) 
{ 
    _root->add_branch(*this); //not rvalue(???) 
} 

Maintenant, le deuxième constructeur créerait un arbre à l'intérieur _root - mais comment cela fonctionne avec l'appel (ignorer violation privée):

_tree Base(0,"base"); 
_tree Branch(1, "branch", &Base); 
Base.subtrees.begin()->value = 8; 
std::cout << Branch.value; 

Comment puis-je faire en sorte que Branch & *Base.subtrees.begin() se réfèrent au même noeud? Ou devrais-je aller dans l'autre sens. Utilisez add_branch() pour créer une branche/sous-arbre?

+5

Les identifiants qui commencent par un trait de soulignement suivi d'une lettre majuscule sont réservés pour l'implémentation. Et regarde moche. –

+0

Qu'est-ce que cela a à voir avec la sémantique de déplacement? – crazyjul

+0

J'ai pris la liberté de changer le titre un peu car il n'était pas très instructif (aussi d'autres changements mineurs). Espérons que cela transmet toujours ce que votre question est. –

Répondre

3

La sémantique de déplacement consiste à déplacer les objets internes d'un objet, pas l'objet lui-même (en tant que pièce de mémoire typée). Il vaut mieux y réfléchir en termes de valeurs et d'invariants, car C++ a toujours une sémantique de valeur même lorsque le déplacement est pris en compte. Cela signifie:

std::unique_ptr<int> first(new int); 
// invariant: '*this is either null or pointing to an object' 
// current value: first is pointing to some int 
assert(first != nullptr); 

// move construct from first 
std::unique_ptr<int> second(std::move(first)); 

// first and second are separate objects! 
assert(&first != &second); 

// New values, invariants still in place 
assert(first == nullptr); 
assert(second != nullptr); 

// this doesn't affect first since it's a separate object 
second.reset(new int); 

En d'autres termes, alors que vous pouvez convertir l'expression *this à un rvalue en faisant std::move(*this) ce que vous voulez ne peut être atteint en ce moment puisque std::list<_tree> utilise la sémantique de valeur et _tree lui-même a la sémantique de valeur. *Base.subtrees.begin() est un objet distinct de Branch et comme les choses sont des modifications à la première n'affectera pas le dernier.

Passez à la sémantique de référence si c'est ce que vous voulez (ou besoin), par exemple en utilisant std::shared_ptr<_tree> et std::enable_shared_from_this (vous utiliserez alors _root->add_branch(shared_from_this()) dans le constructeur). Je ne le recommanderais pas, cela pourrait être désordonné. La sémantique des valeurs est très souhaitable à mon avis.


Avec la sémantique de valeur, en utilisant votre arbre pourrait ressembler à:

_tree Base(0, "base"); 
auto& Branch = Base.addBranch(1, "branch"); 

C'est, addBranch renvoie une référence au nœud nouvellement construit. Saupoudrant sémantique se mettra sur:

_tree Base(0, "base"); 
_tree Tree(1, "branch); // construct a node not connected to Base 
auto& Branch = Base.addBranch(std::move(Tree)); 
// The node we used to construct the branch is a separate object 
assert(&Tree != &Branch); 

strictement parler si _tree est la sémantique de déplacer copiables ne sont pas nécessaires si, Base.addBranch(Tree); fonctionnerait aussi.