c++ - 使用内部迭代器从 boost 多索引中删除项目时的一致性

标签 c++ boost boost-multi-index

假设我正在使用两个索引之一迭代 boost::multi_index。然后,如果我开始使用其他索引进行迭代并根据某些条件删除元素,这是否会使上层迭代器无效?

例如,

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/composite_key.hpp>

#include <string>
#include <iostream>

using namespace boost::multi_index;
using namespace std;

struct person {
    string name;
    string last_name;
    int age;
};

typedef multi_index_container<
    person,
    indexed_by<
        ordered_non_unique<member<person, string, &person::name> >,
        ordered_non_unique<member<person, int, &person::age> >,
        sequenced<>
    >
> PersonCollection;


int main()
{
     PersonCollection pc;
     //insert some elements into pc
     struct person kk = {"John", "Doe", 15};
     pc.insert(kk);
     kk = {"John", "Doe2", 17};
     pc.insert(kk);
     kk = {"John", "Doe3", 34};
     pc.insert(kk);
     kk = {"John", "Smith", 34};
     pc.insert(kk);

     auto &index0 = pc.get<0>();
     auto range0 = index0.equal_range("John");
     while (range0.first != range0.second) {
         auto &index1 = pc.get<1>();
         auto range1 = index1.equal_range(34);
         while (range1.first != range1.second) {
             if (range1.first->last_name == "Smith")
                 range1.first = index1.erase(range1.first);
             else
                 ++range1.first;    
         }
    ++range0.first;
    }
    return 0;
}

那么在这种情况下,range0 迭代器是否始终有效?谢谢!

最佳答案

您的循环可能存在根本性缺陷。我认为这是因为您使用了令人困惑的名称(range0 等)。你的外循环与内循环无关,所以你可以删除它(节省时间做无用的重复)。这是一个明确的重写:

auto& ageIdx = pc.get<byAge>();

auto namerange = pc.get<byName>().equal_range("John");
for (auto name_it = namerange.first, end = namerange.second; name_it != end; ++name_it) {

    auto agerange = ageIdx.equal_range(34);

    for (auto age_it = agerange.first, end = agerange.second; age_it != end;) {
        if (age_it->last_name == "Smith")
            age_it = ageIdx.erase(age_it);
        else
            ++age_it;    
    }
}

确实这是不安全的。 name_it 可能无效。修复它:

for (auto name_it = namerange.first, end = namerange.second; name_it != end;) {
    ++name_it;

Note the code will remove remove 'Ruby Smith (age 34)' just fine. The 'John' criterion is never used, so:

更好的是,将其修复为等效的:

auto agerange = pc.get<byAge>().equal_range(34);

for (auto age_it = agerange.first, end = agerange.second; age_it != end;) {
    std::cout << *age_it << "\n";
    if (age_it->last_name == "Smith")
        age_it = ageIdx.erase(age_it);
    else
        ++age_it;    
}

跳出框框思考

看起来你真的想通过一些“主”键(姓名、姓氏、年龄)删除。说出你的意思:

pc.erase(pc.find(boost::make_tuple("Smith", "John", 34))); // just the one

这就是所有的代码。

Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <string>
#include <iostream>

struct person {
    std::string name;
    std::string last_name;
    int age;

    friend std::ostream& operator<<(std::ostream& os, person const& p) {
        return os << "{" << p.name << ", " << p.last_name << ", " << p.age << "}";
    }
};

namespace bmi = boost::multi_index;

typedef bmi::multi_index_container<
    person,
    bmi::indexed_by<
        bmi::ordered_unique<bmi::tag<struct primaryKey>,
            bmi::composite_key<person,
                bmi::member<person, std::string, &person::last_name>,
                bmi::member<person, std::string, &person::name>,
                bmi::member<person, int,         &person::age>
            >
        >,
        bmi::ordered_non_unique<bmi::tag<struct byAge>,  bmi::member<person, int, &person::age> >
    >
> PersonCollection;

int main() {
    PersonCollection pc {
        person { "John",    "Lennon",    34 },
        person { "Elliot",  "Gardiner",  72 },
        person { "John",    "Smith",     34 },
        person { "Lucy",    "Greenstle", 34 },
        person { "Gold",    "Smith",     34 },
        person { "Nicolai", "Josuttis",  42 }
    };

    auto& idx = pc.get<primaryKey>();

    // print
    std::copy(pc.begin(), pc.end(), std::ostream_iterator<person>(std::cout << "\n", "; "));

    pc.erase(pc.find(boost::make_tuple("Smith", "John", 34))); // just the one
    // print
    std::copy(pc.begin(), pc.end(), std::ostream_iterator<person>(std::cout << "\n", "; "));

    auto range = idx.equal_range(boost::make_tuple("Smith", "John")); // any age
    for (auto f=range.first, l=range.second; f!=l;)
        f = idx.erase(f);
    // print
    std::copy(pc.begin(), pc.end(), std::ostream_iterator<person>(std::cout << "\n", "; "));

    range = idx.equal_range(boost::make_tuple("Smith")); // any age/first name
    for (auto f=range.first, l=range.second; f!=l;)
        f = idx.erase(f);

    // print
    std::copy(pc.begin(), pc.end(), std::ostream_iterator<person>(std::cout << "\n", "; "));
}

打印:

{Elliot, Gardiner, 72}; {Lucy, Greenstle, 34}; {Nicolai, Josuttis, 42}; {John, Lennon, 34}; {Gold, Smith, 34}; {John, Smith, 34}; 
{Elliot, Gardiner, 72}; {Lucy, Greenstle, 34}; {Nicolai, Josuttis, 42}; {John, Lennon, 34}; {Gold, Smith, 34}; 
{Elliot, Gardiner, 72}; {Lucy, Greenstle, 34}; {Nicolai, Josuttis, 42}; {John, Lennon, 34}; {Gold, Smith, 34}; 
{Elliot, Gardiner, 72}; {Lucy, Greenstle, 34}; {Nicolai, Josuttis, 42}; {John, Lennon, 34}; 

关于c++ - 使用内部迭代器从 boost 多索引中删除项目时的一致性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33828458/

相关文章:

c++ - 链接 zmq c++ header 时出错

java - Android 从耳机/USB 插孔发送/接收字节?

c++ - glGetAttribLocation() 返回 -1 但变量是统一的,如何解决?

c++ - 来自绑定(bind)方法的原始函数指针

c++ - 如何检测 boost tcp 套接字何时断开连接

c++ - 如何将 vector 拆分为 n 个 "almost equal"部分

c++ - 使用 boost::multi_index reverse_iterators

c++ - 在 boost 的 multi_index_container 中获取不等于 x 的值

c++ - boost 对散列唯一索引的多索引访问

c++ - 将集合论应用于 C++11 可变参数模板