如何解决自定义地图运算符的迭代器-> 使用虚拟引用的重载
我有一个带有(整数索引,值)对的自定义类似地图的容器。这是我的迭代器实现:
// _v is a vector holding the data with next indices
typedef int ind; // index type
// T is the value type
class iterator {
private:
idmap& owner;
std::pair<ind,T&> _p;
public:
iterator(idmap& ic,ind x) : owner(ic),_p{x,*(T*)(&_p)} {}
iterator& operator++() {
_p.first = owner._v[_p.first]._next;
return *this;
}
// the star* operator is straight forward
std::pair<ind,T&> operator*() const {
return std::pair<ind,T&>(_p.first,owner._v[_p.first]._t);
}
// the -> operator is where the weirdness comes in
std::pair<ind,T&>* operator->() const {
// placement new seems to reconstruct the pair without
// accessing the old dangling T& reference. Assignmet construction
// (_p = std::pair<>...) ended up corrupting data.
new(&const_cast<iterator*>(this)->_p) std::pair<ind,owner._v[_p.first]._t);
return &(const_cast<iterator*>(this)->_p);
}
bool operator!=(const iterator& other) {
return (&other.owner != &owner || other._p.first != _p.first);
}
bool operator==(const iterator& other) {
return (&other.owner == &owner && other._p.first == _p.first);
}
};
为了实现箭头操作符->,我需要返回一个指向配对类型又名std::pair<ind,T&>*
的指针(指向活动配对的指针)。为了得到这个,我在迭代器中有一个本地 std::pair<x,T&> _p
,我将它复制初始化到迭代下的当前对,并在 operator-> 中返回它。
为了解决 T&
中未初始化的 _p
引用的问题,我已将其初始化为 *(T*)(&_p)
(_p
不是有效的 T
对象!)超级草图,但我很确定在 operator->
中更新之前迭代器永远不会访问它。你觉得这有什么问题吗,除了奸诈之外?
编辑 1:
该代码适用于迭代,但是,一旦访问该引用,悬空引用就会导致问题。例如,相互分配两个新的迭代器会导致 std::pair 的复制构造函数启动并访问引用损坏数据。
编辑 2:
我最终使用 boost::variant<boost::blank,std::pair...>
来允许“未初始化”的指针。当前代码为:
class iterator {
private:
idmap& owner;
ind x;
boost::variant<boost::blank,std::pair<ind,T&>> _p;
public:
iterator(idmap& ic,x(x) {}
iterator& operator++() {
x = owner._v[x]._next;
return *this;
}
const std::pair<ind,T&>& operator*() const {
const_cast<iterator*>(this)->_p = boost::blank();
const_cast<iterator*>(this)->_p = std::pair<ind,T&>(x,owner._v[x]._t);
return boost::get<std::pair<ind,T&>>(const_cast<iterator*>(this)->_p);
}
std::pair<ind,T&>* operator->() const {
const_cast<iterator*>(this)->_p = boost::blank();
const_cast<iterator*>(this)->_p = std::pair<ind,owner._v[x]._t);
return &(boost::get<const std::pair<ind,T&>&>(const_cast<iterator*>(this)->_p));
}
bool operator!=(const iterator& other) {
return (&other.owner != &owner || other.x != x);
}
bool operator==(const iterator& other) {
return (&other.owner == &owner && other.x == x);
}
};
这适用于迭代并且迭代器现在是可复制的。现在的问题是:
for (const auto& x : idmp) {
x.second = T();
}
编译没有错误,即使 const auto&
应该捕获一个 const std::pair
。这就是 std::unordered_map
在相同情况下的表现。有什么想法吗?
编辑3:
更好的想法是使用字节存储并使用placement new 将std::pair
保留在迭代器中,摆脱boost 变体:
class iterator {
private:
idmap& owner;
ind x;
static constexpr uint szp = sizeof(std::pair<ind,const T&>);
static constexpr uint szpr = sizeof(std::pair<ind,const T&>);
static constexpr uint sum_size = boost::static_unsigned_max<szp,szpr>::value + 1;
uint8_t _p[sum_size];
public:
iterator(idmap& ic,T&>& operator*() const {
new (const_cast<iterator*>(this)->_p) std::pair<ind,owner._v[x]._t);
return *((std::pair<ind,T&>*)const_cast<iterator*>(this)->_p);
}
std::pair<ind,T&>* operator->() const {
new (const_cast<iterator*>(this)->_p) std::pair<ind,owner._v[x]._t);
return (std::pair<ind,T&>*)const_cast<iterator*>(this)->_p;
}
bool operator!=(const iterator& other) {
return (&other.owner != &owner || other.x != x);
}
bool operator==(const iterator& other) {
return (&other.owner == &owner && other.x == x);
}
};
for(const auto& x : idmp)
的行为仍然不像 std::map
那样,也就是 x
变为只读。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。