Boost.Container flat_map 和 std::string_view

Posted

技术标签:

【中文标题】Boost.Container flat_map 和 std::string_view【英文标题】:Boost.Container flat_map and std::string_view 【发布时间】:2018-05-10 03:22:00 【问题描述】:

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

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

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

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

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

【问题讨论】:

异构查找和“兼容键”确实是 boost 部分存在的特性(例如 Boost 多索引)。也许这些关键字可以帮助您找到您似乎记得的内容。否则安德烈的答案非常好 多索引:这可能是我看到它的地方,因为我也使用过它。谢谢。 【参考方案1】:

Boost.Container 仅添加了对多态查找函数的支持 recently。如果一切正常,它应该与 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();

【讨论】:

为什么需要element_order 而不是只使用less&lt;&gt; 因为std::less 不是多态函数对象。它要求其参数与其模板参数相同。 实际上,从 C++14 开始,std::less&lt;void&gt;(以及等价的std::less&lt;&gt;)似乎是多态的。但是您仍然无法使用它,因为您需要访问element_type 的成员,而std::less 会尝试将整个element_type 与密钥进行比较。【参考方案2】:

也许这不是您所指的,但是如果您使用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&lt;&gt;的比较器):

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

【讨论】:

我认为使用string_view 作为密钥类型是一个非常糟糕的主意,因为它不拥有存储空间。 (不过,如果您控制添加内容的方式,可以限制曝光;也许是另一个集合的交叉索引) 某事不是一个坏主意,因为在某些情况下它不适用。它根本不适用于所有情况。 (顺便说一句,我刚刚修复了一个我在睡觉后发现的错误。干杯:)) 我不明白将不同的集合作为模板参数传递给 flat_map 会有什么帮助。 没人这么说。我想你可能还没有完全阅读答案,所以我鼓励你这样做,我会在早上看到你的 cmets。晚安! 您说:flat_map 的存储容器参数比基于具有模板化 find 成员的排序向量编写字典更好。我没有完整阅读哪些内容?

以上是关于Boost.Container flat_map 和 std::string_view的主要内容,如果未能解决你的问题,请参考以下文章

boost::container::vector 和 std::vector 有啥区别

.select_on_container_copy_construction 左侧的 C++ boost::container::vector 必须具有类/结构/联合

Boost::multi_index_container 具有不同的键和元素类型

boost flat_map 批量插入

boost::container::allocator_traits::is_partially_propagable 是啥意思?

flat_map 的神秘行为