मैं नेस्टेड इटरेटर टेम्पलेट लिखने की कोशिश कर रहा हूं। तो विचार यह है कि आप std::array<std::array<int, N> M>> के int s पर पुनरावृति कर सकते हैं जैसे कि यह एक एकल निरंतर सरणी हो। लेकिन टेम्प्लेट सभी प्रकार के अन्य संयोजनों के साथ भी काम करेगा।

यहाँ मेरे कोड का एक सरलीकृत संस्करण है, जिसमें अभी एक छोटी सी समस्या है:

#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 प्रकारों के साथ काम करे?

0
J. Schultke 26 मई 2020, 18:02

1 उत्तर

सबसे बढ़िया उत्तर

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&>()));
1
Piotr Skotnicki 26 मई 2020, 15:24