如何将 std::move class 和 std::vector<class> 成员转换成另一个 vector<class>?

How to std::move class with std::vector<class> member into another vector<class>?

我正在尝试将 'Node' classes 的层次结构线性化为单个 (std::vector) 数组。这是演示问题的完整 C++ 程序代码,已尽可能简化:

#include <iostream>
#include <vector>

struct Node;

struct B{
    int nvar1;
    std::vector<Node> Children;
};

struct Node{
    B bvar1;
};

void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
    std::cout<<"Linearizing started.\n";
    ArrayOfNodes.push_back(std::move(NODE));
    Node & node = ArrayOfNodes.back();
    for(int n = 0; n < node.bvar1.Children.size(); n++){
        std::cout<<"Running loop "<<n<<" of "<<node.bvar1.Children.size()<<"\n";
        Linearize(node.bvar1.Children[n], ArrayOfNodes);
    }
    std::cout<<"Done with node linearization.\n";
}


int main(){
    Node ParentNode;

    //Fill the ParentNode
    ParentNode.bvar1.nvar1 = 0;
    ParentNode.bvar1.Children.resize(2);
    ParentNode.bvar1.Children[0].bvar1.Children.resize(2);
    ParentNode.bvar1.Children[0].bvar1.Children[0].bvar1.nvar1 = 1;
    ParentNode.bvar1.Children[0].bvar1.Children[1].bvar1.nvar1 = 2;
    ParentNode.bvar1.Children[1].bvar1.nvar1 = 3;

    std::cout<<"I do get to the linearizing.\n";
    std::vector<Node> ArrayOfNodes;
    Linearize(ParentNode, ArrayOfNodes);

    std::cout<<"I do get to the displaying part.\n";
    for(int n = 0; n < ArrayOfNodes.size(); n++){
        std::cout<<ArrayOfNodes[n].bvar1.nvar1<<"\n";
    }

    return 0;
}

这会使程序崩溃。崩溃前的输出是:

I do get to the linearizing.
Linearizing started.
Running loop 0 of 2
Linearizing started.
Runnning loop 0 of 2
Linearizing started.
Done with node linearization.
Done with node linearization.
Running loop 1 of 18446744073709191157
Linearizing started.
Running loop 0 of 1011712
Linearizing started.

我想在这里找到一个优雅高效的解决方案。 'Node' class 可以变大并包含许多其他 classes 和向量。考虑到数据大小,我不愿意构造 move constructors/assignments 来覆盖所有数据结构。

我想做的事情将适用于此代码:

void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
    std::cout<<"Linearizing started.\n";
    ArrayOfNodes.push_back(NODE);
    Node & node = ArrayOfNodes.back();
    for(int n = 0; n < NODE.bvar1.Children.size(); n++){
        std::cout<<"Running loop "<<n<<" of "<<node.bvar1.Children.size()<<"\n";
        Linearize(NODE.bvar1.Children[n], ArrayOfNodes);
    }
    std::cout<<"Done with node linearization.\n";
}

但是当我想移动它时,那会复制东西。我希望它比这更有效率。

基本上是两个问题(s/组):

  1. 如果调用默认移动构造函数,为什么节点不能正确移动到 ArrayOfNodes?默认的移动构造函数不是调用每个成员的移动构造函数,而且 std::vector 里面有指针,所以移动时它应该仍然指向相同的数据?我误解了流程的哪一部分?

  2. 对于这种情况(线性化),standard/good/veteran 编码器的解决方案是什么?

欢迎任何评论,这是我的第一个问题,如果我做错了什么或者可以做得更好,请告诉我。谢谢!

发生的事情是 node 是对您正在构建的 std::vector 中的项目的引用(即指针)。获取引用后,您在向量上使用 push_back,这将增加底层数组,因此可能会使所有指向它的指针无效(增加数组通常意味着分配一个新的、更大的内存块,并将所有数据移动到它)。当您随后想要访问节点的下一个子节点时,您正在引用释放的内存。

有 3 种方法可以解决这个问题。首先,在开始线性化过程之前预先分配数组:

std::vector<Node> ArrayOfNodes;
ArrayOfNodes.reserve(numberOfNodes); // <-- you need to be able to determine this
Linearize(ParentNode, ArrayOfNodes);

第二种解决方案是在移动节点之前将子项推到向量上:

void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
    std::cout<<"Linearizing started.\n";
    for(int n = 0; n < NODE.bvar1.Children.size(); n++){
        std::cout<<"Running loop "<<n<<" of "<< NODE.bvar1.Children.size()<<"\n";
        Linearize(NODE.bvar1.Children[n], ArrayOfNodes);
    }
    ArrayOfNodes.push_back(std::move(NODE));
    std::cout<<"Done with node linearization.\n";
}

第三种解决方案是不引用向量中的节点,而是获取其索引:

void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
    std::cout<<"Linearizing started.\n";
    size_t index = ArrayOfNodes.size();
    ArrayOfNodes.push_back(std::move(NODE));
    for(int n = 0; n < ArrayOfNodes[index].bvar1.Children.size(); n++){
        std::cout<<"Running loop "<<n<<" of "<<ArrayOfNodes[index].bvar1.Children.size()<<"\n";
        Linearize(ArrayOfNodes[index].bvar1.Children[n], ArrayOfNodes);
    }
    std::cout<<"Done with node linearization.\n";
}

一种完全不同的方法是根本不移动节点,而是构造一个 std::vector<Node*>,并用指向节点的指针填充它。但这可能不是你想要的。