Переместить семантику и ссылочную семантику - PullRequest
2 голосов
/ 09 октября 2011

Я пишу древовидный контейнер, где у каждого «узла» есть список с ветвями / поддеревьями, в настоящее время моя голова выглядит так:

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(???)
}

Теперь второй конструктор создаст дерево внутри _root - однако, как это работает с вызовом (игнорируйте частное нарушение):

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

Как бы я сделал так, чтобы Branch & *Base.subtrees.begin() ссылались на один и тот же узел? Или я должен пойти другим путем. Используйте add_branch(), чтобы СОЗДАТЬ ветку / поддерево?

1 Ответ

3 голосов
/ 09 октября 2011

Семантика перемещения - это перемещение внутренних объектов объекта, а не самого объекта (как типизированного фрагмента памяти). Лучше думать об этом с точки зрения значений и инвариантов, поскольку в C ++ семантика значений по-прежнему сохраняется, даже если учитывать перемещение Это значит:

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);

Другими словами, хотя вы можете преобразовать выражение *this в rvalue, выполнив std::move(*this), то, чего вы хотите, не может быть достигнуто прямо сейчас, поскольку std::list<_tree> использует семантику значений, а _tree сама имеет семантику значений. *Base.subtrees.begin() является отличным от Branch объектом, и, поскольку вещи являются модификациями, первое не повлияет на второе.

Переключитесь на ссылочную семантику, если это то, что вам нужно (или нужно), например, с помощью std::shared_ptr<_tree> и std::enable_shared_from_this (тогда вы будете использовать _root->add_branch(shared_from_this()) внутри конструктора). Я не рекомендовал бы это, хотя, это могло бы стать грязным. Семантика значения очень желательна, на мой взгляд.


При семантике значений использование вашего дерева может выглядеть следующим образом:

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

То есть addBranch возвращает ссылку на вновь созданный узел. Опрыскивая некоторую семантику перемещения сверху:

_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 );

Строго говоря, если _tree является копируемой, то семантика перемещения не нужна, однако Base.addBranch(Tree); также будет работать.

...