代码之家  ›  专栏  ›  技术社区  ›  MK.

封装STD::向量允许迭代,但没有其他

c++
  •  8
  • MK.  · 技术社区  · 7 年前

    我想在我的类中隐藏一个向量场,但允许通过它的元素轻松迭代,而不允许其他任何东西。所以这个班的客户可以

    for (auto element : foo.getElements()) { }
    

    但不是

    foo.getElements()[42];
    

    有没有一些简单的方法来实现这个w/o创建新的令人困惑的类型?

    4 回复  |  直到 7 年前
        1
  •  14
  •   Nicol Bolas    7 年前

    我不能说什么是什么,什么不是“新的令人困惑的类型”。但这足以满足基于范围的需求 for 以下内容:

    template<typename Iter>
    class iterator_range
    {
    public:
      iterator_range(Iter beg, Iter end) : beg_(beg), end_(end) {}
    
      Iter begin() const {return beg_;}
      Iter end() const {return end_;}
    
    private:
      Iter beg_, end_;
    };
    

    范围ts为构成“范围”的内容增加了更多的复杂性,但这对于基于范围的内容来说已经足够好了。 对于 .所以你的 foo.getElements 函数如下所示:

    auto getElements()
    {
      return iterator_range<vector<T>::iterator>(vec.begin(), vec.end());
    }
    
    auto getElements() const
    {
      return iterator_range<vector<T>::const_iterator>(vec.begin(), vec.end());
    };
    
        2
  •  5
  •   Drew Dormann    7 年前

    您可以使用 高阶函数 只公开迭代功能:

    class something
    {
    private:
        std::vector<item> _items;
    
    public:
        template <typename F>
        void for_items(F&& f)
        {
            for(auto& i : _items) f(i);
        }
    };
    

    用法:

    something x;
    x.for_items([](auto& item){ /* ... */ });
    

    这种模式的优点是:

    • 易于实现(不需要任何“代理”类);
    • 可以透明地更改 std::vector 在不破坏用户的情况下对其他东西。

    为了完全正确和学究,你必须揭露三种不同的 参考合格 的版本 for_items .例如。:

    template <typename F>
    void for_items(F&& f) &      { for(auto& i : items) f(i); }
    
    template <typename F>
    void for_items(F&& f) const& { for(const auto& i : items) f(i); }
    
    template <typename F>
    void for_items(F&& f) &&     { for(auto& i : items) f(std::move(i)); }
    

    上述代码确保 const -正确性并允许元素在 something 实例是临时的。

        3
  •  3
  •   lubgr    7 年前

    这里有一个基于代理的方法(尽管我不确定新类型是否满足不混淆的要求)。

    template<class Container> class IterateOnlyProxy {
        public:
            IterateOnlyProxy(Container& c) : c(c) {}
    
            typename Container::iterator begin() { return c.begin(); }
            typename Container::iterator end() { return c.end(); }
    
        private:
            Container& c;
    };
    

    代理用作 getElements() 方法,

    class Foo {
        public:
            using Vec = std::vector<int>;
            using Proxy = IterateOnlyProxy<Vec>;
    
            Proxy& getElements() { return elementsProxy; }
    
        private:
            Vec elements{4, 5, 6, 7};
            Proxy elementsProxy{elements};
    };
    

    客户机代码可以在底层容器上迭代,但这就是问题所在。

    Foo foo;
    
    for (auto element : foo.getElements())
        std::cout << element << std::endl;
    
    foo.getElements()[42]; // error: no match for ‘operator[]’
    
        4
  •  0
  •   4386427    7 年前

    如果您希望在类中隐藏一个向量字段,但仍要执行基于范围的for循环,则可以根据添加自己的迭代器 vector::iterator .

    一个简单的(不完整的)例子可以是:

    #include <iostream>
    #include <vector>
    
    class Foo
    {
        public:
        class iterator
        {
            public:
            iterator(std::vector<int>::iterator n) : p(n) {}
            bool operator==(iterator& rhs) { return p == rhs.p; }
            bool operator!=(iterator& rhs) { return p != rhs.p; }
            iterator& operator++() { p++; return *this; }
            int& operator*() { return *p; }
    
            private:
            std::vector<int>::iterator p;
        };
    
        iterator begin() { return iterator(v.begin()); }
        iterator end() { return iterator(v.end()); }
    
        private:
        std::vector<int> v {1, 2, 3, 4, 5};
    };
    
    int main() {
        Foo foo;
        for(auto y : foo) std::cout << y << std::endl; 
        return 0;
    }