1

一段时间以来,我一直使用 Boostflat_map作为我的首选关联集合,原因解释在他们的文档介绍中引用,并且(最初)它在编译器的 std 实现之前提供了更新的功能,并且跨平台是相同的.

现在,我想开始使用string_view来防止复制字符串,当这些字符串取自较大输入的子字符串时。 string_view指向较大字符串中的一系列字符,而无需将它们复制到新std::string实例中。

在找到要使用的地图时,我想起了我过去喜欢的 Boost.Container 的另一个渐进式功能是conformal keys,您可以在其中使用与存储的密钥进行正确比较的任何内容,而不是转换为实际类型的关键。

但现在我在文档中找不到任何提及。我知道std::map现在可以做到这一点(自 C++14 起),但我宁愿将 flat_map 用于小型集合。

几年前,如果在等方面不明显,我能看到什么允许这种灵活性boost::flat_map::insert现在有哪些好的平面集合可以与最新的编译器一起使用?

4

2 回答 2

4

对多态查找函数的支持最近才添加到 Boost.Container 中。如果一切都很好,它应该与 Boost 1.68 一起发布。

同时,您可以使用有序的std::vectorstd::lower_bound.

typedef std::pair< std::string, int > element_type;
std::vector< element_type > map;

struct element_order
{
    bool operator()(element_type const& left, element_type const& right) const
    {
        return left.first < right.first;
    }

    bool operator()(std::string_view const& left, element_type const& right) const
    {
        return left < right.first;
    }

    bool operator()(element_type const& left, std::string_view const& right) const
    {
        return left.first < right;
    }
};

auto find_element(std::string_view const& key)
{
    auto it = std::lower_bound(map.begin(), map.end(), key, element_order());
    if (it != map.end() && it->first == key)
        return it;
    return map.end();
}
于 2018-05-10T14:58:42.207 回答
0

也许这不是您所指的,但是如果您std::string_view用作键类型,则所有操作都已通过隐式转换为std::string_view

Live On Coliru

#include <boost/container/flat_map.hpp>
#include <string_view>

int main() {
    boost::container::flat_map<std::string_view, int> m {
        { "one", 1 },
        { "two", 2 },
        { "three", 3 },
        { "four", 4 },
    };

    std::string key = "one";
    auto one = m.at(key);
    auto range = m.equal_range(key);
    auto it = m.find(key);

    m[key] = 1;
}

在这里,您实际上需要使用一个确实支持兼容键查找的容器。滚动一个不需要过于复杂:

这是一个:

Live On Coliru

#include <initializer_list>
#include <algorithm>
#include <utility>
#include <stdexcept>

#include <boost/container/small_vector.hpp>

template <typename K, typename V, typename Cmp = std::less<K>, typename Storage = boost::container::small_vector<std::pair<K, V>, 10> >
struct flat_map {
    using key_type       = K;
    using mapped_type    = V;
    using key_compare    = Cmp;

    using storage        = Storage;
    using value_type     = typename storage::value_type;
    using iterator       = typename Storage::iterator;
    using const_iterator = typename Storage::const_iterator;

    struct value_compare {
        key_compare _cmp;
        template <typename A, typename B>
        bool operator()(A const& a, B const& b) const { return _cmp(access(a), access(b)); }

      private:
        static auto& access(value_type const& v) { return v.first; }
        template <typename Other>
        static auto& access(Other const& v)      { return v; }
    } _cmp;

    storage _data;

    flat_map(std::initializer_list<value_type> i) : _data(i) {}

    iterator begin()             { return _data.begin(); }
    iterator end()               { return _data.end();   }
    const_iterator begin() const { return _data.begin(); }
    const_iterator end()   const { return _data.end();   }

    template <typename Key>
    mapped_type& operator[](Key&& key) { return find(std::forward<Key>(key))->second; }
    template <typename Key>
    mapped_type const& operator[](Key&& key) const { return find(std::forward<Key>(key))->second; }

    template <typename Key>
    iterator find(Key&& key) {
        auto r = equal_range(std::forward<Key>(key));
        return (r.first == r.second)? end() : r.first;
    }
    template <typename Key>
    const_iterator find(Key&& key) const {
        auto r = equal_range(std::forward<Key>(key));
        return (r.first == r.second)? end() : r.first;
    }

    template <typename Key>
    mapped_type& at(Key&& key) {
        auto r = equal_range(std::forward<Key>(key));
        if (r.first == r.second) throw std::out_of_range("key");
        return r.first->second;
    }
    template <typename Key>
    mapped_type const& at(Key&& key) const {
        auto r = equal_range(std::forward<Key>(key));
        if (r.first == r.second) throw std::out_of_range("key");
        return r.first->second;
    }

    template <typename Key>
    auto equal_range(Key&& key) { return std::equal_range(begin(), end(), std::forward<Key>(key), _cmp); }
    template <typename Key>
    auto equal_range(Key&& key) const { return std::equal_range(begin(), end(), std::forward<Key>(key), _cmp); }
};

它正好支持第一个场景的逆(给定的比较器std::less<>):

#include <string_view>
#include <string>

int main() {
    flat_map<std::string, int, std::less<> > m {
        { "one", 1 },
        { "two", 2 },
        { "three", 3 },
        { "four", 4 },
    };

    std::string_view key = "one";
    auto one = m.at(key);
    auto range = m.equal_range(key);
    auto it = m.find(key);

    m[key] = 1;
}
于 2018-05-10T21:10:33.780 回答