作为 boost::multi_index::sequenced<> 偏移量函数的索引检索

问题描述

我被迫使用 std::vector 容器作为我的底层数据结构。我正在尝试利用 boost::multi_index::sequenced<> 来隐藏向量偏移,并为更丰富的数据查询提供一种机制。我真的不想不必要地将所有数据从一个容器复制到另一个容器。

在下面的示例代码片段中,我有一个 BarInterface 类,用于管理元素到 Bar::foos 容器的插入和删除。最初我尝试将向量元素的引用存储为 typedef boost::multi_index_container 的元素,但这些对插入不稳定。

我想做的是有一个自定义密钥提取器,它是 _bar 容器和 boost::multi_index::sequenced<> 索引的函数。因此,例如要获取元素的名称,我会找到序列偏移量 x,然后是 _bar.foos[x].name。因此,我实际上只是使用 boost::multi_index 作为对可变大小向量进行更丰富查询的代理。

struct Foo {
    std::string name;
};

struct Bar {
    std::vector<Foo> foos;
};

class BarInterface {
public:
    struct Dummy {};
    BarInterface(Bar& bar) : _bar(bar) {
        for (auto& foo : bar.foos) {
            _idx.get<1>().insert(Dummy{});
        }
    }

    // Index
    struct Name {};
    typedef boost::multi_index_container<
        Dummy,// We're not actually storing anything here
        boost::multi_index::indexed_by<
            boost::multi_index::sequenced<>,// Kept inline with vector order!
            boost::multi_index::ordered_unique<
                boost::multi_index::tag<Name>,// I need something here that takes the boost::multi_index::sequenced<>
                // key to get the offset x,and then returns this->_bar.foos[x].name!
            >
        >
    > MultiIndex;

    // Insert
    void insert(const Foo& foo) {
        _bar.foos.push_back(foo);
        _idx.get<1>().insert(Dummy{});
    }

    // Remove
    template <typename T>
    void remove(T it) {
        _bar.foos.erase(_bar.foos.begin() + std::distance(_idx.begin(),_idx.project<0>(it)));
        _idx.erase(_idx.project<0>(it));
    }

protected:
    Bar& _bar;
    MultiIndex _idx;
};

我知道 boost::multi_index 支持各种键提取器——用于成员变量、成员函数、全局函数等。但是,我似乎找不到一个示例来说明如何将键生成boost::multi_index::sequenced<> 索引的函数。这是可能的,还是有一个优雅的替代方案?

解决方法

这是极其脆弱的,我不建议使用此类代码进行生产,但是既然您要求这样做:

Live Coliru Demo

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <iostream>
#include <string>
#include <vector>

struct Foo {
    std::string name;
};

struct Bar {
    std::vector<Foo> foos;
};

class BarInterface;

struct Dummy
{
    Dummy(const Foo& x): p(&x){}
    Dummy(const Dummy&): p(nullptr){}
    
    const Foo* p;
};

struct NameExtractor
{
    NameExtractor(BarInterface* p): p(p){}
    
    using result_type=std::string;
    
    const result_type& operator()(const Dummy& d)const;

    BarInterface* p;
};

class BarInterface {
public:
    BarInterface(Bar& bar) :
        _idx(boost::make_tuple(
            boost::tuple<>(),boost::make_tuple(NameExtractor(this),std::less<std::string>())
        )),_bar(bar)
    {
        for (auto& foo : bar.foos) {
            _idx.get<1>().insert(bar.foos.back());
        }
    }

    // Index
    struct Name {};
    typedef boost::multi_index_container<
        Dummy,// We're not actually storing anything here
        boost::multi_index::indexed_by<
            boost::multi_index::random_access<>,// Kept inline with vector order!
            boost::multi_index::ordered_unique<
                boost::multi_index::tag<Name>,NameExtractor
            >
        >
    > MultiIndex;

    // Insert
    void insert(const Foo& foo) {
        _bar.foos.push_back(foo);
        _idx.get<1>().insert(_bar.foos.back());
    }

    // Remove
    template <typename T>
    void remove(T it) {
        _bar.foos.erase(_bar.foos.begin() + std::distance(_idx.begin(),_idx.project<0>(it)));
        _idx.erase(_idx.project<0>(it));
    }
    
    void remove(const char* name) {
        remove(_idx.get<1>().find(name));
    }
    
    void print()const
    {
        auto key=_idx.get<1>().key_extractor();
      
        for(const auto& d: _idx.get<1>()){
            std::cout<<key(d)<<" ";
        }
        std::cout<<"\n";
    }

protected:
    friend NameExtractor;
  
    Bar& _bar;
    MultiIndex _idx;
};

const NameExtractor::result_type& NameExtractor::operator()(const Dummy& d)const
{
    if(d.p){
        return d.p->name;
    }
    else{
        std::size_t offset=p->_idx.iterator_to(d)-p->_idx.begin();
        return p->_bar.foos[offset].name;
    }
}

int main()
{
    Bar bar;
    BarInterface bi(bar);
    
    bi.insert(Foo{"hello"});
    bi.insert(Foo{"bye"});
    bi.insert(Foo{"Boost"});
    
    bi.print();
    

    bi.remove("bye");

    bi.print();

    bi.insert(Foo{"MultiIndex"});
    
    bi.print();
}

输出

Boost bye hello 
Boost hello 
Boost MultiIndex hello