封装std :: vector以允许迭代,但没有别的

时间:2018-06-11 17:48:28

标签: c++

我想在我的类中隐藏一个矢量字段,但允许通过其元素轻松迭代,但没有别的。这样班级的客户就可以做到

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

但不是

foo.getElements()[42];

是否有一些简单的方法可以实现此目的而不会创建新的混淆类型?

4 个答案:

答案 0 :(得分:14)

我不能说什么是“新混淆型”。但这足以满足基于范围的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增加了构成“范围”的复杂性,但这对于基于范围的for来说已经足够了。所以你的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());
};

答案 1 :(得分:5)

您可以使用高阶函数来仅显示迭代功能:

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的三个不同的 ref-qualified 版本。 E.g:

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实例为临时时移动元素。

答案 2 :(得分:3)

这是一种基于代理的方法(虽然我不确定新类型是否符合不混淆的要求)。

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[]’

答案 3 :(得分:0)

如果要在类中隐藏矢量字段但仍然基于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;
}