自定义地图运算符的迭代器-> 使用虚拟引用的重载

问题描述

我有一个带有(整数索引,值)对的自定义类似地图的容器。这是我的迭代器实现:

    // _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 (将#修改为@)