无法获得与std ::: less等效的嵌套迭代器

问题描述 投票:0回答:1

我正在尝试编写一个嵌套的迭代器模板。因此,您的想法是,您可以遍历intstd::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类型实例化时,endconst方法变得模棱两可。

default_range的想法是,它像默认的std::less一样工作,但用户可以决定通过调用rbegin来自己制作。

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

那么我该如何实现default_range使其适用于const和非const类型?

c++ templates iterator c++17 constexpr
1个回答
1
投票

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&>()));
© www.soinside.com 2019 - 2024. All rights reserved.