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::vector
和std::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<>
?
因为std::less
不是多态函数对象。它要求其参数与其模板参数相同。
实际上,从 C++14 开始,std::less<void>
(以及等价的std::less<>
)似乎是多态的。但是您仍然无法使用它,因为您需要访问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<>
的比较器):
#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::container::allocator_traits::is_partially_propagable 是啥意思?