移动语义和引用语义

发布于 2024-12-08 15:55:49 字数 1141 浏览 2 评论 0原文

我正在编写一个类似树的容器,其中每个“节点”都有一个包含分支/子树的列表,目前我的头看起来像:

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()创建分支/子树?

I'm writing a tree-like container, where each "node" has a list with branches/subtrees, currently my head looks like:

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

Now the second constructor would create a tree inside _root - however how does this work with calling (ignore private violation):

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

How would I make it so that Branch & *Base.subtrees.begin() refer to the same node? Or should i go the other way. Use add_branch() to CREATE a branch/subtree?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

千仐 2024-12-15 15:55:51

移动语义是关于移动对象的内部,而不是对象(作为一段类型的内存)本身。最好从值和不变量的角度来考虑它,因为即使考虑到移动,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);

换句话说,虽然您可以通过执行 std::move(*this) 将表达式 *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); 也可以。

Move semantics is about moving the internals of an object, not the object (as a typed piece of memory) itself. It's best to think about it in terms of values and invariants, since C++ still has value semantics even when moving is taken into account. This means:

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

In other words, while you can convert the expression *this to an rvalue by doing std::move(*this) what you want cannot be achieved right now since std::list<_tree> uses value semantics and _tree has value semantics itself. *Base.subtrees.begin() is a distinct object from Branch and as things are modifications to the former won't affect the latter.

Switch to reference semantics if that's what you want (or need), for instance by using std::shared_ptr<_tree> and std::enable_shared_from_this (you'd then use _root->add_branch(shared_from_this()) inside the constructor). I wouldn't recommend it though, this could get messy. Value semantics are very desirable in my opinion.


With value semantics, using your tree could look like:

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

That is, addBranch returns a reference to the newly constructed node. Sprinkling some move semantics on top:

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

Strictly speaking if _tree is copyable move semantics are not necessary though, Base.addBranch(Tree); would work too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文