如何使用ordered_non_unique索引保持有序

How can I preserve sequenced order with an ordered_non_unique index?

本文关键字:索引 unique 何使用 ordered non      更新时间:2023-10-16

我有一个boost::multi_index_container,由ordered_non_unique键索引,并排序。当对非唯一索引进行迭代时,会按照添加到容器中的顺序而不是它们在序列中的位置出现。

如何设置非唯一索引以保持插入顺序?我试着用ordered_non_unique和sequenced制作一个composite_key,但由于sequenced不是一个键索引,它不会编译。

这里有一个最小的例子(实时版本在这里):

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <iostream>
#include <vector>
using namespace boost::multi_index;
using namespace std;
struct Entry {
    int nonUniqueInt;
    string somethingExtra;
};
using Container_t = multi_index_container<Entry, indexed_by<
    sequenced<>,
    ordered_non_unique<
        // ***** What can I put here? *****
        member<Entry, int, &Entry::nonUniqueInt>
    >
>>;
std::vector<Entry> const sampleData{
    {1, "first"}, {2, "second"}, {3, "third"}, {3, "fourth"}, {2, "fifth"}, {1, "sixth"}
};
// fillFront should make the sequence LIFO
template <typename T>
void fillFront(T & container) {
    for (auto & item : sampleData) {
        container.push_front(item);
    }
}
// fillBack should make the sequence FIFO
template <typename T>
void fillBack(T & container) {
    for (auto & item : sampleData) {
        container.push_back(item);
    }
}
int main() {
    Container_t container;
    auto & sequenced = container.get<0>();
    auto & ordered = container.get<1>();
    fillFront(sequenced);
    for(auto & entry : ordered) {
        cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
    }
    cout << endl;
    container.clear();
    fillBack(sequenced);
    for(auto & entry : ordered) {
        cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
    }
}
// Expected/desired output:   Actual output:
//  1: sixth                   1: first
//  1: first                   1: sixth
//  2: fifth                   2: second
//  2: second                  2: fifth
//  3: fourth                  3: third
//  3: third                   3: forth
//           
//  1: first                   1: first
//  1: sixth                   1: sixth
//  2: second                  2: second
//  2: fifth                   2: fifth
//  3: third                   3: third
//  3: forth                   3: forth

您希望在有序索引中,对于相等的键,项"保持稳定"。

Boost Multi Index不支持。你所能做的"最好"就是按照迭代器的外观在插入顺序索引中排序。

使用random_access索引。

using Container_t = multi_index_container<Entry, indexed_by<
    random_access<>,
    ordered_non_unique< member<Entry, int, &Entry::nonUniqueInt> >
>>;

下面是一个示例:

int main() {
    Container_t container;
    auto & sequenced = container.get<0>();
    fillFront(sequenced);
    stabled_ordered(container, [](Entry const& entry) {
            cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
        });
    cout << endl;
    container.clear();
    fillBack(sequenced);
    stabled_ordered(container, [](Entry const& entry) {
            cout << entry.nonUniqueInt << ": " << entry.somethingExtra << endl;
        });
}

查看Live On Coliru

神奇的当然是stabled_ordered,它是std::for_each的修改版本,接受一个容器和一个函子:

template <typename Container, typename F, int RA = 0, int ONU = 1>
    F stabled_ordered(Container const& container, F&& f);

实现迭代order - nonunique索引(由ONU模板参数表示)并调用函子,但在具有等效键的范围内以插入顺序(由RA (random_access)表示)调用:

template <typename Container, typename F, int RA = 0, int ONU = 1>
    F stabled_ordered(Container const& container, F&& f)
{
    using RAIt  = typename Container::template nth_index<RA> ::type::const_iterator;
    using ONUIt = typename Container::template nth_index<ONU>::type::const_iterator;
    auto& ordered = container.template get<ONU>();
    for(ONUIt cursor = ordered.begin(); cursor != ordered.end(); )
    {
        // get range with equiv. keys
        auto key_range = ordered.equal_range(ordered.key_extractor()(*cursor));
        cursor = key_range.second;
        // project into first index
        std::vector<RAIt> v;
        for(auto it = key_range.first; it != key_range.second; ++it)
            v.push_back(boost::multi_index::project<RA>(container, it));
        // put into original order
        std::sort(v.begin(), v.end());
        for_each(v.begin(), v.end(), [&f](RAIt const& it) { f(*it); });
    }
    return std::forward<F>(f);
}

不要被typename .... ::template咒语吓倒:这些只是因为我想让算法实现比你可能需要的更通用:)