问题描述
我想实现一个迭代器,以便在for range循环中使用自定义类。迭代器访问基类的std::vector
的内部std::unique_ptr
,并返回指向子类的原始指针。
这是我想出的:
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()]); }
};
这在最新的编译器(在GodBolt上使用 GCC , Clang 和 MSVC 进行了测试)上很好用,但是在使用 Visual Studio 2015 ,end()
方法会引发运行时异常:
Debug assertion failed. C++ vector subscript out of range.
我在互联网上搜索了一种正确的方法来访问std::vector
的单末尾元素的地址,但是除了复杂的指针运算法则之外什么也没找到。
我终于为begin()
和end()
方法提出了以下实现:
iterator begin() { return iterator(&list.front()); }
iterator end() { return iterator(&list.back() + 1); }
运行时不会抱怨。是访问std::array
或std::vector
的单末尾元素地址的正确方法吗?
如果没有,正确的方法是什么?
解决方法
正确的方法是什么?
您正在尝试重新发明轮子。您无需为class iterator
实现Test
,因为您可以从list
获取 begin 和 end 迭代器(即 std::vector<upBase>::begin
和 std::vector<upBase>::end
)
因此,只需通过Test
类中的相应成员函数使它们可用:
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(); }
};
还要注意,auto
自c++14起才可能返回。如果编译器不支持C ++ 14,则可以将其提供为trailing return type,如下所示(假设至少您可以访问c++11):
auto begin() -> decltype(list.begin()) { return list.begin(); }
auto end() -> decltype(list.end()) { return list.end(); }