使用内部迭代器从提升多索引中删除项目时的一致性

Consistency when removing items from boost multi-index using an inner iterator

假设我正在使用两个索引之一迭代 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;    
}

跳出框框思考

看起来你真的想通过一些 "primary" 键(名字,last_name,年龄)删除。说出你的意思:

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