我可能有点晚了,但是我想要添加一个答案,灵感来自@TobiasRibizel的回答。我们不需要在你的struct
中添加许多样板代码,而是在结构体的指定成员上一次性添加更多的样板代码,形成一个迭代器。
#include <iostream>
#include <string>
#include <map>
template<class C, typename T, T C::* ...members>
class struct_it {
public:
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::bidirectional_iterator_tag;
constexpr struct_it (C &c) : _index{0}, _c(c)
{}
constexpr struct_it (size_t index, C &c) : _index{index}, _c(c)
{}
constexpr static struct_it make_end(C &c) {
return struct_it(sizeof...(members), c);
}
constexpr bool operator==(const struct_it& other) const {
return other._index == _index;
}
constexpr bool operator!=(const struct_it& other) const {
return !(other == *this);
}
constexpr T& operator*() const {
return _c.*_members[_index];
}
constexpr T* operator->() const {
return &(_c.*_members[_index]);
}
constexpr struct_it& operator--() {
--_index;
return *this;
}
constexpr struct_it& operator--(int) {
auto copy = *this;
--_index;
return copy;
}
constexpr struct_it& operator++() {
++_index;
return *this;
}
constexpr struct_it& operator++(int) {
auto copy = *this;
++_index;
return copy;
}
private:
size_t _index;
C &_c;
std::array<T C::*, sizeof...(members)> _members = {members...};
};
template<class C, typename T, T C::* ...members>
using cstruct_it = struct_it<const C, T, members...>;
struct boundary {
int top;
int bottom;
int left;
int right;
using iter = struct_it<boundary, int, &boundary::top, &boundary::bottom, &boundary::left, &boundary::right>;
using citer = cstruct_it<boundary, int, &boundary::top, &boundary::bottom, &boundary::left, &boundary::right>;
iter begin() {
return iter{*this};
}
iter end() {
return iter::make_end(*this);
}
citer cbegin() const {
return citer{*this};
}
citer cend() const {
return citer::make_end(*this);
}
};
int main() {
boundary b{1,2,3,4};
for(auto i: b) {
std::cout << i << ' ';
}
std::cout << '\n';
}
它适用于C++14,在C++11上,
constexpr
函数默认都是
const
的,所以它们不能工作,但只需去掉
constexpr
就可以解决问题。好处在于您可以选择一些
struct
成员并对其进行迭代。如果您始终要迭代相同的几个成员,则可以添加
using
。这就是为什么我选择将指向成员的指针作为模板的一部分,即使实际上并不需要,因为我认为只有相同成员上的迭代器才应该是相同类型。
您还可以保持原样,将
std::array
替换为
std::vector
,并在运行时选择要迭代哪些成员。