0

I want to make a container class that should be allowed to be iterable through a for each loop, but only through for each loops. I don't want to grant access to its .begin() and .end() methods.

Is such a thing possible, maybe through overloading and friendship of std::begin and std::end methods?

I've made some attempts, one of which looks something like the following. The compiler has always complained about the privacy of .begin() and .end(), though.

namespace std {

    MyIter begin (MySealedContainer&);
    MyIter end (MySealedContainer&);

}

class MyIter {
    // ...
};

class MySealedContainer {
    friend MyIter std::begin (MySealedContainer&);
    friend MyIter std::end (MySealedContainer&);

    private:
        MyIter begin();
        MyIter end();

    // ...
};

// ---

MyIter std::begin (MySealedContainer& c) {
    return c.begin();
}

MyIter std::end (MySealedContainer& c) {
    return c.end();
}

Even with a private .begin() and .end(), I gotta be able to do the following:

MySealedContainer foo;
// Insert elements...

for (auto& each: foo) {
    // Do something with each.
}
Mutoh
  • 358
  • 3
  • 18

1 Answers1

0

Using friendship to grant access to std::begin and std::end will not provide any benefits. Other code will be free to use them to gain access to the adapters iterators making the whole approach useless. Eventually it ends up like MySpace and nobody wants to use it anymore. Eventually you end up like Facebook with everyone abusing it and doing things you don't want them to do.

The only option for handling begin/end would be to grant access to individual classes and free functions through friendship. Unfortunately this will impose limitations on its use and will require updating every time you want to grant access to additional functions. The following example highlights the futility of using friendship to give access to a free function like std::begin

class Restricted
{
    int begin() { return 0; }

    friend int std_begin(Restricted&r);
};

int std_begin(Restricted&r)
{
    return r.begin();
}


int main()
{
    Restricted  building;

    // Side step private! might as well just call building.begin()
    int it = std_begin(building);
}

[Old unneeded answer left for historical insignificance]

If you want to limit access I recommend implementing for_each as one or more member functions of the class. This takes a functor as one of the parameters and iterates through the container. This makes it generally available to anyone that wants to use it while still imposing restrictions on accessing data. The following example provides a for_each function and a functor to play with.

#include <iostream>
#include <vector>

class Adapter
{
public:

    template<typename FuncType>
    void for_each(FuncType &func)
    {
        for(std::vector<int>::iterator it = data_.begin();
            it != data_.end();
            ++it)
        {
            // Pass by value (prevent modification)
            // you can pass the iterator as is if you like!
            func(*it); 
        }
    }
    //private: we leave it public for demo purposes
    std::vector<int>    data_;
};

int main()
{
    Adapter cnt;

    cnt.data_.push_back(1);
    cnt.data_.push_back(2);
    cnt.data_.push_back(3);
    cnt.data_.push_back(4);

    struct {
        void operator()(int value) {
            std::cout << value << std::endl;
        }
    } for_function;

    cnt.for_each(for_function);
}

You will want to add const qualified versions of the for_each function and possible a few overloads with different number of parameters depending on your requirements. With C++11 you have the option of passing a lambda or using std::function and you can also use components included in Boost.

Captain Obvlious
  • 19,754
  • 5
  • 44
  • 74
  • I am aware of this option, but I want to know if I'm able to iterate through my class with c++11's built-in for each, and still have its contents be protected from individual access. I just find it much more elegant than functors or lambdas for foreaching. – Mutoh May 25 '13 at 22:24
  • Ah. you want to use it with c++11's range based for statements. Ok that's a different store. I'll update my answer to include a solution for that as well – Captain Obvlious May 25 '13 at 22:26
  • You should update your question to make it clear you want range based for statements and tag it with C++11. – Captain Obvlious May 25 '13 at 22:27
  • i'm updated it and unfortunately granting friendship to `std::begin` and `std::end` will gain nothing. – Captain Obvlious May 25 '13 at 22:56
  • Alright then, I'll have to work my way around my limitations. It won't be very hard, though. Thank you very much. (y) – Mutoh May 25 '13 at 23:03