【问题标题】:emplace_back when loop of the same listemplace_back 当循环同一个列表
【发布时间】:2013-01-18 13:02:06
【问题描述】:

我向你展示我的问题

我有 2 个列表,分别命名为 A 和 B。

list<vector<int> > A = {{1},{2},{3}};
list<vector<int> > B = {{4},{5},{6}}; 

我想要的是 A = {{1,4},{1,5},{1,6},{2,4},{2,5},{2,6},{3 ,4},{3,5},{3,6}} 不使用任何 tmp 列表。

我在 Ubuntu 12.04 上使用 C++11 和 gcc 4.6.3

这样最小化代码:

auto A_begin = A.begin();
auto A_end = A.end();
auto B_begin = B.begin();
auto B_end = B.end();

for(auto i = A_begin; i != A_end; ++i) //loop on A
{
    for (auto j = B_begin;j != B_end; ++j) //loop on B
    {
        vector<int> tmp = (*i); // A[i]
        copy((*j).begin(),(*j).end(),back_inserter(tmp)); // append B[j] to A[i]
        A.emplace_back(tmp); //add it to A
    }
}
A.erase(A_begin,A_end); // remove {1},{2},{3}

所以,我认为算法没问题,但它会在 A 上产生无限循环。 我认为是当我制作 A.emplace_back 时 A_end 发生了变化,但我保存了它,所以我真的不知道在这里追加什么。

我的代码来识别问题:

auto A_begin = A.begin();
auto A_end = A.end();
auto B_begin = B.begin();
auto B_end = B.end();

int ii = A.size();

for(auto i = A_begin; i != A_end; ++i) //loop on A
{
    for (auto j = B_begin;j != B_end; ++j) //loop on B
    {
        vector<int> tmp = (*i);
        A.emplace_back(tmp);
    }
    cout<<--ii<<endl; // exit when print 0 ?
}

这个打印负数,我又要^C了。

编辑:我找到了解决方案:

auto A_begin = A.begin();
auto A_end =  A.end();
auto B_begin = B.begin();
auto B_end = B.end();

list<vector<int>> tmp_l;

for(auto i = A_begin; i != A_end; ++i) //loop on A
{
    for (auto j = B_begin;j != B_end; ++j) //loop on B
    {
        vector<int> tmp = (*i); // A[i]
        copy((*j).begin(),(*j).end(),back_inserter(tmp)); // append B[j] to A[i]
        tmp_l.emplace_back(move(tmp)); //add it to A
    }
}
 swap(tmp_l,A);

【问题讨论】:

    标签: c++ list loops vector c++11


    【解决方案1】:

    这两行:

    vector<int> tmp = (*i); // A[i]
    copy((*j).begin(),(*j).end(),tmp.end()); // append B[j] to A[i]
    

    将调用未定义的行为。通过复制到 tmp.end(),您只是在 A[i] 结束后覆盖内存,而不是扩展 A[i]。您需要使用 back_insert 迭代器,例如:

    vector<int> tmp = (*i); // A[i]
    copy((*j).begin(), (*j).end(), back_inserter(tmp)); // append B[j] to A[i]
    

    您还需要包含标题以获取 back_inserter。

    编辑:此外,A_end 迭代器指向列表的“结束”位置,因此无论您添加多少项,它们总是添加到 A_end 前面,因此是无限循环。我不确定是否有解决此问题的好方法。不创建临时列表没有任何好处,无论哪种方式,您都在分配相同的内存,只需写入一个新列表即可。

    【讨论】:

    • 感谢 back_inserter,但没有变化:/
    • 这正是我所怀疑的。但是,tmp 列表的副本会很长(我里面有很多对象),所以我尽量不复制它。
    • tmp 列表不是副本,而是列表。将输出写入一个新列表,然后使用 std::swap 交换 A 和 tmp 的内容。这是一个非常便宜的操作,它不会复制内容,只需移动几个指针。然后当你的函数退出时 tmp 将包含 A 的旧内容并被破坏。
    • 我添加了一个编辑来发布解决这个问题的解决方案,没有 tmp。
    【解决方案2】:

    你的算法不好。

    这个:

    copy((*j).begin(),(*j).end(),tmp.end());
    

    会导致各种问题,因为你覆盖了一些随机内存。

    你可能想做这样的事情来追加:

    vector<int> tmp = (*i);
    copy((*j).begin(),(*j).end(),std::back_inserter(tmp));
    

    【讨论】:

    • 谢谢,但是没有usign copy,我也有同样的问题。
    • 呼叫保留将不起作用。复制完成后,向量的大小不会更新,因此您仍然要写到向量的末尾。
    【解决方案3】:

    编辑:我找到了解决方案:

    该解决方案很好,使用临时向量并将其与 A 交换比在原地执行它更好,因为您的原始版本(以及复制到向量末尾)以 erase 结尾,它移动 每个 元素。

    但您的解决方案可以改进:

    // get rid of these iterators, they're useless
    /*
    auto A_begin = A.begin();
    auto A_end =  A.end();
    auto B_begin = B.begin();
    auto B_end = B.end();
    */
    
    list<vector<int>> tmp_l;
    
    // use new for loops
    for (auto& a : A)
    {
        for (auto& b : B)
        {
    // use auto
            auto tmp = a; // A[i]
    // I find just inserting at the end simpler than using `back_inserter`
            tmp.insert(tmp.end(), b.begin(), b.end()); // append B[j] to A[i]
    // then clear it the moved-from elements:
            b.clear();
    // move the tmp vector into place, do not copy it.
            tmp_l.emplace_back(std::move(tmp));
        }
    }
    swap(tmp_l,A);
    

    【讨论】:

    • 很好,使用 move 是个好主意,但是 b 在循环内,所以,它是第一个 a,而不是其他的。
    猜你喜欢
    • 2016-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多