另一种选择是通过使用多个容器来分离查找和数据管理:
std::unordered_map<string_view, value> map
std::vector<unique_ptr<const char[]>> mapKeyStore
查找使用无需分配的
string_view
进行。每当插入新键时,我们需要首先添加一个真正的字符串分配:
mapKeyStore.push_back(conv(str))
map.emplace(mapKeyStore.back().get(), value)
使用
std::string
在
mapKeyStore
中会更加直观。然而,使用
std::string
不能保证字符串内存不变(例如,如果向量调整大小)。使用
unique_ptr
可以强制执行这一点。但是,我们需要一些特殊的转换/分配例程,在示例中称为
conv
。如果您有一个自定义的字符串容器,可以保证在移动时数据一致性(并强制向量使用移动),则可以在此处使用它。
缺点
上述方法的缺点是处理删除操作非常棘手且昂贵,如果采用简单的方法进行删除,则会增加开销。如果地图仅创建一次或仅增长,则此问题不存在,上述模式运行良好。
运行示例
以下示例包括对一个键的简单删除。
#include <vector>
#include <unordered_map>
#include <string>
#include <string_view>
#include <iostream>
#include <memory>
#include <algorithm>
using namespace std;
using PayLoad = int;
unique_ptr<const char[]> conv(string_view str) {
unique_ptr<char[]> p (new char [str.size()+1]);
memcpy(p.get(), str.data(), str.size()+1);
return move(p);
}
int main() {
unordered_map<string_view, PayLoad> map;
vector<unique_ptr<const char[]>> mapKeyStore;
mapKeyStore.push_back(conv("a"));
map.emplace(mapKeyStore.back().get(), 3);
mapKeyStore.push_back(conv("b"));
map.emplace(mapKeyStore.back().get(), 1);
mapKeyStore.push_back(conv("c"));
map.emplace(mapKeyStore.back().get(), 4);
cout << map.find("a")->second;
cout << map.find("b")->second;
cout << map.find("c")->second;
map.erase("a");
mapKeyStore.erase(remove_if(mapKeyStore.begin(), mapKeyStore.end(),
[](const auto& a){ return strcmp(a.get(), "a") == 0; }),
mapKeyStore.end());
cout << '\n';
for(auto it : map)
cout << it.first << ": " << it.second << "\n";
return 0;
}
当然,这两个容器可以放入一个包装器中,该包装器处理自己的插入和删除。
string_view
类通常被称为string_ref
。 - SergeyAstring_view
中提供一个转换运算符,将std::string
引用指向data
呢?operator std::string&() { return *data; }
这样就不需要分配内存了。 - PaulMcKenziestring_view
表示字符串的一部分,而不是整个字符串。 - T.C.hash<int>
和hash<short>
对于相同的值返回不同的结果。 - T.C.