Here is a proxy-based approach (though I'm not sure whether the new type meets the requirement of not being confusing).
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;
};
The proxy is used as a return type for the getElements()
method,
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};
};
and client code can iterate over the underlying container, but that's about it.
Foo foo;
for (auto element : foo.getElements())
std::cout << element << std::endl;
foo.getElements()[42]; // error: no match for ‘operator[]’