0

i have two objects that look like this:

class Object
{
};

class List
{
public:
    std::vector<std::shared_ptr<Object>> list;
};

and i want to be able to create a range-based Loop like this:

for (Object& object : list) {
    //...
}

for that i created the begin() and end() members:

class List
{
public:

    std::vector<std::shared_ptr<Object>> list;

    Object& begin() { return *list.begin()->get(); }
    const Object& begin() const { return *list.begin()->get(); }
    Object& end() { return *list.end()->get(); }
    const Object& end() const { return *list.end()->get(); }
};

but the IDE is now asking me for the operators *, ++ and != and i dont undestand how i am supossed to implement can i get some help with that?

Dolfos
  • 98
  • 1
  • 9
  • 3
    You have not provided meaningful `begin` and `end`. They should return iterators, not objects. Also, dereferencing `end` iterator is UB, but you won't need that piece of code. – LogicStuff Apr 03 '18 at 14:59
  • @LogicStuff then how do i convert the shared_ptr to a reference? – Dolfos Apr 03 '18 at 15:01
  • 1
    @Dolfos You will need to write an iterator type for `List`. Trying to convert your `shared_ptr`s to references instead of using an iterator type would be a mistake. – François Andrieux Apr 03 '18 at 15:03

3 Answers3

3

The range-for protocol requires that begin() and end() return a type that conforms to the standard library's Iterator concept. This includes things like being able to dereference the iterator with operator*, and increment it with operator++.

An object reference, as you are returning from your begin(), does not meet these requirements, which is what the compiler is complaining about.

Writing your own iterator is quite tedious, especially if you want to support all the random-access facilities that std::vector's iterators offer. There are libraries that can make this a bit easier though.

A far more straightforward way to do things would be to simply forward the iterators that your list member already gives, i.e.

class List
{
public:
    using iterator = std::vector<shared_ptr<Object>>::iterator;
    using const_iterator = std::vector<shared_ptr<Object>>::const_iterator;

    std::vector<std::shared_ptr<Object>> list;

    iterator begin() { return list.begin(); }
    const_iterator begin() const { return list.begin(); }
    iterator end() { return list.end(); }
    const_iterator end() const { return list.end(); }
};

Now you can use a List in a range-for loop by saying

for (auto& optr : list) {
    do_something_with_object(*optr);
}

The only catch is that dereferencing the iterator will give you a reference to a shared_ptr<Object>, not an Object&, so you'll need to dereference this again to actually use the object itself (as above).

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
1

Range-based for loops need more than some arbitrary objects to be returned by begin() and end().

for (auto& object : list) { ... }

for your list is per definition more or less equivalent to the following:

{
    auto iterator = list.begin();
    auto end = list.end();
    for ( ; iterator != end; ++iterator)
    {
        auto& object = *iterator;
        ...
    }
}

That is why your compiler asks you for all these operations (!=, ++, *), as it tries to apply them to the object returned by begin().

The problem is that you are expected to provide an iterator, not just the first and last element of your list. You cannot give the compiler two different Objects and expect it to be able to loop through a list of pointers to them (which is not necessarily visible from the loop).

Tristan provided you with some options in his answer. I would add that the ranges library can remove a lot of the tedium from writing your own iterators, but it is most likely overkill for your situation and probably not the easiest to get accustomed with.

Max Langhof
  • 23,383
  • 5
  • 39
  • 72
0

If you grasped previous replies, I rewrite them in a short snippet. No need for any typedef:

class List {
public:
    std::vector<std::shared_ptr<Object>> list;
    auto begin() { return list.begin(); }
    auto begin() const { return list.begin(); }
    auto end() { return list.end(); }
    auto end() const { return list.end(); } 
};
Red.Wave
  • 2,790
  • 11
  • 17