MultiIndex 容器——提供向量和集合访问

MultiIndex containers -- offering vector and set access

我有一个应用程序,首先生成一个 std::vector<int> 对象。然后,需要对这个被视为 std::set<int> 的对象执行一些操作,其中顺序无关紧要,重复也不算数。

目前,我从std::vector<int> 对象明确构造了一个std::set<int> 类型的对象。示例如下:

#include <cstdio>
#include <set>
#include <vector>

void printset(std::set<int>& Set) {
    printf("Printing Set Elements: ");
    for (std::set<int>::iterator siter = Set.begin(); siter != Set.end(); ++siter) {
        int val = *siter;
        printf("%d ", val);
    }
    printf("\n");
}

void printvec(std::vector<int>& Vec) {
    printf("Printing Vec Elements: ");
    for (size_t i = 0, szi = Vec.size(); i < szi; ++i) {
        int val = Vec[i];
        printf("%d ", val);
    }
    printf("\n");
}

int main()
{
    std::vector<int> VecInt{ 6, 6, 5, 5, 4, 4 };
    std::set<int> SetInt(VecInt.begin(), VecInt.end());
    printvec(VecInt);
    printset(SetInt);
}

我想看看我是否可以使用 Boost.MultiIndex 来达到这个目的。 One introduction to Boost.MultiIndex 状态:

Boost.MultiIndex can be used if elements need to be accessed in different ways and would normally need to be stored in multiple containers. Instead of having to store elements in both a vector and a set and then synchronizing the containers continuously, you can define a container with Boost.MultiIndex that provides a vector interface and a set interface.

这正是我正在做的事情(使用多个容器,然后不断地从另一个容器创建一个),而我想创建一个(多索引)容器一次,然后提供一个向量接口和一个集合接口.

查看各种示例,例如 here and ,不清楚如何将这些示例修改为上面的代码示例。

理想情况下,我想在上面的代码示例中执行以下操作:

MultiIndexContainer vec_set_container;

vec_set_container.push_back(6);//or anything equivalent for the MultiIndexContainer
vec_set_container.push_back(6);
vec_set_container.push_back(5);
vec_set_container.push_back(5);
vec_set_container.push_back(4);
vec_set_container.push_back(4);

printvec(vec_set_container.Some_Function_That_Exposes_Vector_Interface());
printset(vec_set_container.Some_Function_That_Exposes_Set_Interface());

如何使用 Boost.MultiIndex 完成此操作?

随机访问索引将匹配“矢量”界面。

有序的唯一索引将匹配“集合”界面。

但是,如果您有唯一索引,这将防止插入重复项。所以,你会得到:

Live On Compiler Explorer

#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index_container.hpp>
#include <fmt/ranges.h>

namespace bmi = boost::multi_index;

using Table = bmi::multi_index_container< //
    int,
    bmi::indexed_by<
        bmi::random_access<bmi::tag<struct asVector>>,
        bmi::ordered_unique<bmi::tag<struct asSet>, bmi::identity<int>>>>;

int main()
{
    Table data{ 6, 6, 5, 5, 4, 4 };

    fmt::print("As vec {}\nAs set {}\n", //
               data.get<asVector>(),    //
               data.get<asSet>());
}

打印

As vec {6, 5, 4}
As set {4, 5, 6}

现在,我认为你可以用它做的“最好的”是制作订单索引 non-unique(所以,模仿 std::multiset std::set): Live On Compiler Explorer

bmi::ordered_non_unique<bmi::tag<struct asSet>, bmi::identity<int>>

打印

As vec [6, 6, 5, 5, 4, 4]
As set {4, 4, 5, 5, 6, 6}

如果要遍历唯一元素,使用范围适配器的成本最低:

  1. 使用提升 Live

    fmt::print("As vec {}\nAs set {}\n", //
               data.get<asVector>(),     //
               data.get<asSet>() | boost::adaptors::uniqued);
    
  2. 使用 RangeV3 Live

    fmt::print("As vec {}\nAs set {}\n", //
               data.get<asVector>(),     //
               data.get<asSet>() | ranges::views::unique);
    
  3. 使用标准范围;我不能 make this work 但它确实应该是 std::ranges::unique(data.get<asSet>())

全部印刷

As vec {6, 6, 5, 5, 4, 4}
As set {4, 5, 6}

其他想法

如果您不需要随机访问,那么 sequenced 索引可能更适合您。请注意,此接口附带方便的 unique() and sort() methods(就像 std::list)。

更新评论

这是对评论的 rolled-in-one 回复:

Live On Compiler Explorer

#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
#include <fmt/ranges.h>
#include <random>

namespace bmi = boost::multi_index;

template <typename T>
using ListSet = bmi::multi_index_container< //
    T,
    bmi::indexed_by<
        bmi::sequenced<bmi::tag<struct byInsertion>>,                   //
        bmi::ordered_unique<bmi::tag<struct Ordered>, bmi::identity<T>> //
        >>;

int main()
{
    ListSet<int> data;

    std::mt19937 prng{99}; // "random" seed
    for (std::uniform_int_distribution d(1, 10); data.size() < 5;) {
        int el = d(prng);
        if (auto [it, unique] = data.push_back(el); not unique) {
            fmt::print("Element {} duplicates index #{}\n", el,
                    std::distance(begin(data), it));
        }
    }

    fmt::print("By insertion {}\nOrdered {}\n", data, data.get<Ordered>());
}

版画

Element 9 duplicates index #3
Element 8 duplicates index #1
By insertion [7, 8, 5, 9, 1]
Ordered {1, 5, 7, 8, 9}