【发布时间】:2021-09-28 05:18:36
【问题描述】:
我查阅了很多答案,但我仍然没有找到最好的实现方法。
现在我的情况是实现一个多地图容器,代码如下:
#include <unordered_map>
#include <utility>
static const size_t MAP_NUM_BITS = 5;
static const size_t MAP_NUM = (size_t)1 << MAP_NUM_BITS;
template<typename K, typename V>
class MultiMap {
using MapType = std::unordered_map<K, V>;
using MapIter = typename MapType::iterator;
using MapRef = typename MapType::reference;
public:
struct iterator {
MapIter it;
size_t idx;
MapType *multi_maps;
bool operator==(const iterator &rhs) const {
return it == rhs.it;
}
bool operator!=(const iterator &rhs) const {
return it != rhs.it;
}
iterator& operator++() {
++it;
while (it == multi_maps[idx].end() &&
idx + 1 < MAP_NUM) {
it = multi_maps[++idx].begin();
}
return *this;
}
iterator operator++(int) {
iterator ret = *this;
++*this;
return ret;
}
MapIter& operator->() {
return it;
}
MapRef operator*() {
return *it;
}
};
iterator begin() {
size_t idx = 0;
auto it = multi_maps_[idx].begin();
while (it == multi_maps_[idx].end() &&
idx + 1 < MAP_NUM) {
it = multi_maps_[++idx].begin();
}
return {it, idx, multi_maps_};
}
iterator end() {
return {multi_maps_[MAP_NUM - 1].end(), MAP_NUM - 1, multi_maps_};
}
iterator find(const K &key) {
size_t hash = hasher_(key);
size_t idx = compute_idx(hash);
auto it = multi_maps_[idx].find(key);
if (it == multi_maps_[idx].end()) {
return end();
}
return {it, idx, multi_maps_};
}
std::pair<iterator, bool> insert(const std::pair<K, V> &data) {
size_t hash = hasher_(data.first);
size_t idx = compute_idx(hash);
auto res = multi_maps_[idx].insert(data);
return {{res.first, idx, multi_maps_}, res.second};
}
size_t size() const {
size_t total_size = 0;
for (auto &m : multi_maps_) {
total_size += m.size();
}
return total_size;
}
void clear() {
for (auto &m : multi_maps_) {
m.clear();
}
}
private:
size_t compute_idx(size_t hash) const {
if (MAP_NUM == 1) {
return 0;
} else {
return hash >> (sizeof(size_t) * 8 - MAP_NUM_BITS);
}
}
private:
MapType multi_maps_[MAP_NUM];
std::hash<K> hasher_;
};
现在它可以在我的测试中使用:
#include <iostream>
#include "multi_map.h"
int main() {
MultiMap<int, int> mm;
for (size_t i = 0; i < 5; ++i) {
mm.insert({2*i, i*i});
}
std::cout << "Map size is: " << mm.size() << std::endl;
for (size_t i = 0; i < 10; ++i) {
auto it = mm.find(i);
if (it != mm.end()) {
std::cout << "Found: " << i << std::endl;
} else {
std::cout << "Not found: " << i << std::endl;
}
}
for (auto it = mm.begin(); it != mm.end(); ++it) {
std::cout << it->first << " : " << it->second << std::endl;
}
return 0;
}
但是当它引用 const 类型时,事情就变糟了!
所以我要实现一个const_iterator,所以代码是:
include <unordered_map>
#include <utility>
static const size_t MAP_NUM_BITS = 5;
static const size_t MAP_NUM = (size_t)1 << MAP_NUM_BITS;
template<typename K, typename V>
class MultiMap {
using MapType = std::unordered_map<K, V>;
using MapIter = typename MapType::iterator;
using MapConstIter = typename MapType::const_iterator;
using MapRef = typename MapType::reference;
using MapConstRef = typename MapType::const_reference;
public:
struct iterator {
MapIter it;
size_t idx;
MapType *multi_maps;
bool operator==(const iterator &rhs) const {
return it == rhs.it;
}
bool operator!=(const iterator &rhs) const {
return it != rhs.it;
}
iterator& operator++() {
++it;
while (it == multi_maps[idx].end() &&
idx + 1 < MAP_NUM) {
it = multi_maps[++idx].begin();
}
return *this;
}
iterator operator++(int) {
iterator ret = *this;
++*this;
return ret;
}
MapIter& operator->() {
return it;
}
MapRef operator*() {
return *it;
}
};
struct const_iterator {
MapConstIter it;
size_t idx;
const MapType *multi_maps;
bool operator==(const const_iterator &rhs) const {
return it == rhs.it;
}
bool operator!=(const const_iterator &rhs) const {
return it != rhs.it;
}
const_iterator& operator++() {
++it;
while (it == multi_maps[idx].end() &&
idx + 1 < MAP_NUM) {
it = multi_maps[++idx].begin();
}
return *this;
}
const_iterator operator++(int) {
const_iterator ret = *this;
++*this;
return ret;
}
MapConstIter& operator->() {
return it;
}
MapConstRef operator*() {
return *it;
}
};
iterator begin() {
size_t idx = 0;
auto it = multi_maps_[idx].begin();
while (it == multi_maps_[idx].end() &&
idx + 1 < MAP_NUM) {
it = multi_maps_[++idx].begin();
}
return {it, idx, multi_maps_};
}
const_iterator begin() const {
size_t idx = 0;
auto it = multi_maps_[idx].begin();
while (it == multi_maps_[idx].end() &&
idx + 1 < MAP_NUM) {
it = multi_maps_[++idx].begin();
}
return {it, idx, multi_maps_};
}
iterator end() {
return {multi_maps_[MAP_NUM - 1].end(), MAP_NUM - 1, multi_maps_};
}
const_iterator end() const {
return {multi_maps_[MAP_NUM - 1].end(), MAP_NUM - 1, multi_maps_};
}
iterator find(const K &key) {
size_t hash = hasher_(key);
size_t idx = compute_idx(hash);
auto it = multi_maps_[idx].find(key);
if (it == multi_maps_[idx].end()) {
return end();
}
return {it, idx, multi_maps_};
}
const_iterator find(const K &key) const {
size_t hash = hasher_(key);
size_t idx = compute_idx(hash);
auto it = multi_maps_[idx].find(key);
if (it == multi_maps_[idx].end()) {
return end();
}
return {it, idx, multi_maps_};
}
std::pair<iterator, bool> insert(const std::pair<K, V> &data) {
size_t hash = hasher_(data.first);
size_t idx = compute_idx(hash);
auto res = multi_maps_[idx].insert(data);
return {{res.first, idx, multi_maps_}, res.second};
}
size_t size() const {
size_t total_size = 0;
for (auto &m : multi_maps_) {
total_size += m.size();
}
return total_size;
}
void clear() {
for (auto &m : multi_maps_) {
m.clear();
}
}
private:
size_t compute_idx(size_t hash) const {
if (MAP_NUM == 1) {
return 0;
} else {
return hash >> (sizeof(size_t) * 8 - MAP_NUM_BITS);
}
}
private:
MapType multi_maps_[MAP_NUM];
std::hash<K> hasher_;
};
代码重复难以忍受。
对于 const 和 non-const 函数,const_cast 会有所帮助:
const_iterator begin() const {
return const_cast<MultiMap *>(*this)->begin();
}
为此,我必须在 const_iterator 中添加一个构造:
const_iterator(const iterator &it) {
this->it = it.it;
this->idx = it.idx;
this->multi_maps = it.multi_maps;
}
这里的代码重复太多了。
在我的情况下,是否有避免代码重复的最佳解决方案?
【问题讨论】:
-
我通常会创建一个
template<class Type> class iterator_impl;和using iterator = iterator_impl<T>;using const_iterator = iterator_impl<const T>;- 如果我认为这很有意义,我会这样做,以便iterator可以转换为const_iterator,但不能反过来。 -
@TedLyngmo 是的,template_impl 是个好主意,但是在函数
const_iterator begin() const中,如何将non-const begin()返回的迭代器转换为const_iterator? -
一个转换构造函数是
enable_ifd 仅适用于const_iterator(const interator&);将是一种选择。如果iterator_impl成员都是public,则不需要friend关系,否则,您需要。 -
@TedLyngmo 哇,我认为这是个好主意。不过很抱歉打扰你,我对C++不是很熟悉,你能给我举个例子吗?
-
一个问题:为什么会这样?
std::unordered_map<K, V>::iterator& operator->();我期待像std::unordered_map<K, V>::value_type* operator->();这样的东西,因为->的特殊行为,它可能会起作用,但它看起来确实让读者有点困惑 :)
标签: c++