Virtual range initializer in C++ to be used in range-based for loop

5 days ago 3
ARTICLE AD BOX

You can't sensibly return pointers from begin and end, because you can't provide an overload of == for two pointer types, nor can you get an int from the dereference of such a pointer that for is defined to use.

If you have subclasses B and C with different containers, you could have virtual boost::any_range<int> getTestRangeInitializer() = 0 in A. However it could well be quicker to copy the values into a std::vector<int>, and return that.

class A { public: virtual std::vector<int> getTestRangeInitializer() = 0; }; class B : public A { std::list<int> data; public: std::vector<int> getTestRangeInitializer() override { return { std::from_range, data }; } }; class C : public A{ std::set<int> data; public: std::vector<int> getTestRangeInitializer() override { return { std::from_range, data }; }; };

Caleth's user avatar

2 Comments

There is no problem to return and compare pointers as long as the pointers are of the same type or base type. The only difficulty is how to prevent memory leaks by returning new pointers. The comparison between raw pointers and int is of course not possible.

2026-03-30T16:16:46.857Z+00:00

You aren't returning an array. The pointer returned by end is not reachable from the one returned by begin, so the behaviour will be undefined

2026-03-31T06:12:47.98Z+00:00

Range-based for doesn’t work with TestRangeInitializer*. It expects a range object with begin()/end() returning the same iterator type by value. Your design returns pointers and mismatched types (TestRange* vs int), so it cannot compile. Either return a proper range (by value/reference) or wrap your polymorphic iterator in a value-type iterator.

hmu535's user avatar

2 Comments

begin()/end() can return different types since C++17. Also, I'm not sure how to keep the polymorphism without using a pointer to the iterator.

2026-03-30T12:39:41.883Z+00:00

C++17 allows different begin/end types but they must be comparable TestRange* and int aren’t. For polymorphism keep the pointer inside a value-type iterator wrapper: class Iterator { std::unique_ptr<TestRange> impl; public: Iterator(std::unique_ptr<TestRange> p) : impl(std::move(p)) {} int operator*() const { return impl->deref(); } Iterator& operator++() { impl->inc(); return *this; } bool operator!=(const Iterator& other) const { return !impl->equals(*other.impl); } }; This keeps polymorphism internally, makes the iterator a proper val

2026-03-30T12:51:58.663Z+00:00

As others have pointed out, the int end() { return -1; } can be tricky as there is no native comparison between pointers and integers.
Also compilers which don't support C++17 treat different return types on begin() and end() as error.

The probably easiest solution would be a TestRange* end() { return nullptr; }.
One big problem of your shown code is that it would produce memory leaks during every iteration.
You should use smart pointers or return raw pointers or references to instances, that are already stored and kept alive somewhere else, e.g. in a container on the current instance.
Besides that I see no problems with your approach except that it is a bit unusual.

Bonus hint:
With the raw pointers you could even use a covariant signature on your subtypes. I won't be surprised if this feature is unknown as there are only very rare situations where this can be used.
In short: If you return a raw pointer or reference, then even the function signature on the subtypes is allowed to return a narrower type instead of the type defined on the derived signature (TestRangeB* instead of TestRange* and TestRangeInitializerB* instead of TestRangeInitializer*).
Note that this works only with raw pointers and references, but not with any templates like smart pointers. This is the reason why it can be used only in rare situations.

Instead of

class B : public A { class TestRangeB : public TestRange; class TestRangeInitializerB : public TestRangeInitializer { TestRange* begin() override { return new TestRangeB(); } }; TestRangeInitializer* getTestRangeInitializer() { return new TestRangeInitializerB(); }; };

you can declare it as

class B : public A { class TestRangeB : public TestRange; class TestRangeInitializerB : public TestRangeInitializer { TestRangeB* begin() override { return new TestRangeB(); } }; TestRangeInitializerB* getTestRangeInitializer() { return new TestRangeInitializerB(); }; };

Thus anybody who is aware that the TestRangeInitializer and iterator were generated from a B can see and access B-specific details on the returned instances.

Thibe's user avatar

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

Read Entire Article