Stack Overflow Asked by Jean-Joël Borter on January 15, 2021
I wanted to implement an iterator to use a custom class in a for range loop. The iterator access an internal std::vector
of std::unique_ptr
of a Base class and returns a raw pointer to a child class.
This is what I came up with:
using upBase = std::unique_ptr<Base>;
class Test
{
std::vector<upBase> list;
public:
void Add(upBase&& i) { list.push_back(std::move(i)); }
class iterator
{
upBase* ptr;
public:
iterator(upBase* p) : ptr(p) {}
bool operator!=(const iterator& o) { return ptr != o.ptr; }
iterator& operator++() { ++ptr; return *this; }
Child& operator*() { return *(Child*)(*ptr).get(); }
const Child& operator*() const { return *(Child*)(*ptr).get(); }
};
iterator begin() { return iterator(&list[0]); }
iterator end() { return iterator(&list[list.size()]); }
};
This works fine on the latest compilers (tested on GodBolt with GCC, Clang and MSVC) but when using Visual Studio 2015 the end()
method throws a run-time exception:
Debug assertion failed. C++ vector subscript out of range.
I search the internet for a proper way to access the address of the one-past-end element of a std::vector
, but didn’t find anything except complicated pointer arithmetic.
I finally came up with the following implementation for the begin()
and end()
methods:
iterator begin() { return iterator(&list.front()); }
iterator end() { return iterator(&list.back() + 1); }
This doesn’t complain at run-time. Is it the correct way to access the address of the one-past-end element of an std::array
or std::vector
?
If not, what would be the proper way?
What would be the proper way?
You are trying to re-invent the wheel. You do not need to implement the class iterator
for your Test
, as you could get the begin and end iterator from the list
(i.e. std::vector<upBase>::begin
and std::vector<upBase>::end
)
Therefore just make them available via corresponding member functions in Test
class:
class Test
{
std::vector<upBase> list;
public:
void Add(upBase&& i) { list.push_back(std::move(i)); }
auto begin() /* const noexcept */ { return list.begin(); }
auto end() /* const noexcept */ { return list.end(); }
};
Also note that the auto
return is only possible since c++14. If the compiler does not support C++14, you can provide it as trailing return type, as follows (assuming at least you have access to c++11):
auto begin() -> decltype(list.begin()) { return list.begin(); }
auto end() -> decltype(list.end()) { return list.end(); }
Correct answer by JeJo on January 15, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP