如何为按需构造其值的迭代器实现operator->?

Wyz*_*a-- 7 c++ iterator operator-overloading c++11

我有一个C++类,它像一个容器:它有size()operator[]成员函数.存储在容器中的值是std::tuple对象.但是,容器实际上并没有将元组保存在内存中; 相反,它根据以不同形式存储的基础数据按需构建它们.

std::tuple<int, int, int>
MyContainer::operator[](std::size_t n) const {
    // Example: draw corresponding elements from parallel arrays
    return { underlying_data_a[n], underlying_data_b[n], underlying_data_c[n] };
}
Run Code Online (Sandbox Code Playgroud)

因此,返回类型operator[]是临时对象,而不是引用.(这意味着它不是左值,因此容器是只读的;没关系.)

现在我正在编写一个迭代器类,可用于遍历此容器中的元组.我想建模RandomAccessIterator,它依赖于InputIterator,但是InputIterator需要支持表达式i->m(其中i是一个迭代器实例),据我所知,operator->需要一个函数来返回一个指针.

当然,我无法返回指向按需构建的临时元组的指针.想到的一种可能性是将一个元组实例作为成员变量放入迭代器中,并使用它来存储迭代器当前所处的任何值的副本:

class Iterator {
private:
    MyContainer *container;
    std::size_t current_index;

    // Copy of (*container)[current_index]
    std::tuple<int, int, int> current_value;
    // ...
};
Run Code Online (Sandbox Code Playgroud)

但是,更新存储的值将要求迭代器检查其当前索引是否小于容器的大小,以便过去的迭代器不会通过访问基础数组的末尾来导致未定义的行为.这增加了(少量)运行时开销 - 当然不足以使解决方案变得不切实际,但感觉有点不雅.迭代器不应该真正存储任何东西,只能指向它正在迭代的容器的指针以及它内部的当前位置.

是否有一种干净,完善的方式来支持operator->按需构建其值的迭代器类型?其他开发人员将如何做这类事情?

(请注意,我根本不需要支持operator->- 我主要是实现迭代器,以便可以使用C++ 11" for循环遍历容器,并且std::tuple没有任何成员可以通常都希望通过->无论如何访问.但我仍然想要正确地模拟迭代器概念;否则我感觉就像是在偷工减料.或者我应该不打扰?)

kec*_*kec 2

下面的示例依赖于operator->重复应用直到返回指针的事实。我们将Iterator::operator->Contained 对象作为临时对象返回。这会导致编译器重新应用operator->。然后我们Contained::operator->简单地返回一个指向自身的指针。请注意,如果我们不想放入operator->Contained 即时对象,我们可以将其包装在一个辅助对象中,该对象返回指向内部 Contained 对象的指针。

#include <cstddef>
#include <iostream>

class Contained {
    public:
        Contained(int a_, int b_) : a(a_), b(b_) {}
        const Contained *operator->() {
            return this;
        }
        const int a, b;
};

class MyContainer {
    public:
        class Iterator {
                friend class MyContainer;
            public:
                friend bool operator!=(const Iterator &it1, const Iterator &it2) {
                    return it1.current_index != it2.current_index;
                }
            private:
                Iterator(const MyContainer *c, std::size_t ind) : container(c), current_index(ind) {}
            public:
                Iterator &operator++() {
                    ++current_index;
                    return *this;
                }
                // -> is reapplied, since this returns a non-pointer.
                Contained operator->() {
                    return Contained(container->underlying_data_a[current_index], container->underlying_data_b[current_index]);
                }
                Contained operator*() {
                    return Contained(container->underlying_data_a[current_index], container->underlying_data_b[current_index]);
                }
            private:
                const MyContainer *const container;
                std::size_t current_index;
        };
    public:
        MyContainer() {
            for (int i = 0; i < 10; i++) {
                underlying_data_a[i] = underlying_data_b[i] = i;
            }
        }
        Iterator begin() const {
            return Iterator(this, 0);
        }
        Iterator end() const {
            return Iterator(this, 10);
        }
    private:
        int underlying_data_a[10];
        int underlying_data_b[10];
};

int
main() {
    MyContainer c;

    for (const auto &e : c) {
        std::cout << e.a << ", " << e.b << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,要小心:像 `const int &amp;a = it-&gt;a;` 这样的东西通常可以工作,但在这里不行,因为在使用 `a` 时临时对象已经被销毁了。 (2认同)