我正在尝试编写一个嵌套的迭代器模板。因此,您的想法是,您可以遍历int
的std::array<std::array<int, N> M>>
,就好像它是单个连续数组一样。但是该模板还可以与其他各种组合一起使用。
这是我的代码的简化版本,目前有一个小问题:
#include <array>
#include <type_traits>
#include <optional>
template <typename T>
struct default_range {
constexpr auto begin(const T& t) const {
return t.begin();
}
constexpr auto end(const T& t) const {
return t.end();
}
constexpr auto begin(T& t) const {
return t.begin();
}
constexpr auto end(T& t) const {
return t.end();
}
};
template <typename Outer, typename OuterRange>
constexpr auto inner_impl() {
// IMPORTANT: this line is necessary because otherwise OuterRange will always be invoked with the const& version of
// begin().
// Outer must first be captured in a variable so that we preserve constness properly
Outer outer = std::declval<Outer>();
return *std::declval<OuterRange>().begin(outer);
}
template <typename Outer, typename OuterRange>
using inner_t = std::remove_reference_t<decltype(inner_impl<Outer, OuterRange>())>;
template <typename T>
using iterator_value_t = typename std::iterator_traits<T>::value_type;
template <typename OuterIterator,
typename InnerRange = default_range<std::remove_reference_t<iterator_value_t<OuterIterator>>>>
class nested_iterator {
private:
using InnerIterator = decltype (std::declval<InnerRange>().begin(*std::declval<OuterIterator>()));
public:
using self_type = nested_iterator;
using value_type = iterator_value_t<InnerIterator>;
using reference = std::remove_reference_t<value_type> &;
using pointer = std::remove_reference_t<value_type> *;
using iterator_category = std::forward_iterator_tag;
using difference_type = size_t;
private:
struct inner_iterators {
InnerIterator pos;
InnerIterator end;
constexpr inner_iterators(InnerIterator pos, InnerIterator end)
: pos{std::move(pos)}, end{std::move(end)}
{
}
};
OuterIterator outerPos;
OuterIterator outerEnd;
std::optional<inner_iterators> inners = std::nullopt;
InnerRange innerRange;
public:
constexpr nested_iterator(OuterIterator outer, OuterIterator outerEnd)
: outerPos{std::move(outer)}, outerEnd{std::move(outerEnd)}
{
// constructor code here
}
// operator overloads here
}; // class iterator
template <typename Outer,
typename OuterRange = default_range<Outer>,
typename InnerRange = default_range<inner_t<Outer, OuterRange>>>
class nested_iterable {
private:
using Inner = inner_t<Outer, OuterRange>;
public:
using outer_iterator = decltype (std::declval<OuterRange>().begin(std::declval<Outer>()));
using inner_iterator = decltype (std::declval<InnerRange>().begin(std::declval<Inner>()));
using iterator = nested_iterator<outer_iterator, InnerRange>;
private:
Outer *outer;
OuterRange outerRange;
public:
constexpr nested_iterable(Outer &outer) : outer{&outer} {}
constexpr iterator begin() const
{
return {outerRange.begin(*outer), outerRange.end(*outer)};
}
constexpr iterator end() const
{
return {outerRange.end(*outer), outerRange.end(*outer)};
}
};
constexpr void test()
{
const std::array<std::array<int, 5>, 3> arr{};
nested_iterable range{arr};
}
<source>:13:20: error: multiple overloads of 'begin' instantiate to the same signature 'auto (const std::array<std::array<int, 5>, 3> &) const'
constexpr auto begin(T& t) const {
^
<source>:27:39: note: in instantiation of template class 'default_range<const std::array<std::array<int, 5>, 3> >' requested here
return *std::declval<OuterRange>().begin(outer);
...
<source>:110:21: note: while substituting deduced template arguments into function template '<deduction guide for nested_iterable>' [with Outer = const std::array<std::array<int, 5>, 3>, OuterRange = (no value), InnerRange = (no value)]
nested_iterable range{arr};
正如我们可以很容易地看出,default_range
不能为const std::array
实例化,因为当用begin
类型实例化时,end
和const
方法变得模棱两可。
default_range
的想法是,它像默认的std::less
一样工作,但用户可以决定通过调用rbegin
来自己制作。
这似乎是目前拼图中缺少的部分。我也尝试过使用单个begin
代替这两种引用的重载,但这也不起作用。在这种情况下,T
将始终需要作为右值引用。
那么我该如何实现default_range
使其适用于const
和非const
类型?
default_range
情况的部分专业化const T
,在这种情况下,您不会使有问题的功能超载:
template <typename T>
struct default_range<const T> {
constexpr auto begin(const T& t) const {
return t.begin();
}
constexpr auto end(const T& t) const {
return t.end();
}
};
此外,请注意,std::declval
仅可在未评估的上下文中使用。话虽如此,inner_impl
需要重新实现,例如:
template <typename Outer, typename OuterRange>
using inner_impl_t = decltype(*std::declval<OuterRange>().begin(std::declval<Outer&>()));