无法获得等效的 std::less 来用于嵌套迭代器

Can't get an equivalent to std::less to work for nested iterators

本文关键字:less 用于 迭代器 嵌套 std      更新时间:2023-10-16

我正在尝试编写一个嵌套迭代器模板。因此,这个想法是,您可以迭代std::array<std::array<int, N> M>>int,就好像它是一个连续数组一样。但是该模板也可以与各种其他组合一起使用。

这是我代码的简化版本,现在有一个小问题:

#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实例化,因为当使用const类型实例化时,beginend方法变得不明确。

default_range的想法是它的工作方式类似于默认std::less但用户可以决定通过调用rbegin来制作自己的。

这似乎是目前拼图中缺失的部分。我还尝试使用单个begin而不是两种引用的重载,但这也不起作用。在这种情况下,T始终需要是右值引用。

那么我该如何实现default_range以便它适用于const和非const类型呢?

针对const T情况的部分专用化default_range,其中您不会重载有问题的函数:

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&>()));