代码之家  ›  专栏  ›  技术社区  ›  Bernard

shift_right()是如何在C++20中实现的?

  •  0
  • Bernard  · 技术社区  · 5 年前

    在C++20中 <algorithm> shift_left() and shift_right() . 它们都接受任何LegacyForwardIterator。对于 左移() ,规定“移动是按 i ​0 右移() ,规定“如果 ForwardIt last - first - n - 1

    我可以想出一个相当简单的实现方法 左移()

    template <typename ForwardIt>
    constexpr inline ForwardIt shift_left(ForwardIt first, ForwardIt last, typename std::iterator_traits<ForwardIt>::difference_type n) {
        if (n <= 0) return last;
        ForwardIt it = first;
        for (; n > 0; --n, ++it) {
            if (it == last) return first;
        }
        return std::move(it, last, first);
    }
    

    如果 满足LegacyBidirectionAlierator要求,我可以看到 右移() 左移() 对于非双向前向迭代器。

    我已经想出了一个算法,可以利用 [first, first+n) 左移()

    template <typename ForwardIt>
    constexpr inline ForwardIt shift_right(ForwardIt first, ForwardIt last, typename std::iterator_traits<ForwardIt>::difference_type n) {
        if (n <= 0) return first;
        ForwardIt it = first;
        for (; n > 0; --n, ++it) {
            if (it == last) return last;
        }
        ForwardIt ret = it;
        ForwardIt ret_it = first;
        for (; it != last; ++it) {
            std::iter_swap(ret_it, it);
            ret_it++;
            if (ret_it == ret) ret_it = first;
        }
        return ret;
    }
    

    右移()

    0 回复  |  直到 5 年前
        1
  •  7
  •   VLL Mohamed El-Nakeep    4 年前

    https://github.com/danra/shift_proposal/blob/master/shift_proposal.h

    链接来自提案文件: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0769r0.pdf

    #include <algorithm>
    #include <iterator>
    #include <type_traits>
    #include <utility>
    
    template<class I>
    using difference_type_t = typename std::iterator_traits<I>::difference_type;
    
    template<class I>
    using iterator_category_t = typename std::iterator_traits<I>::iterator_category;
    
    template<class I, class Tag, class = void>
    constexpr bool is_category = false;
    template<class I, class Tag>
    constexpr bool is_category<I, Tag, std::enable_if_t<
        std::is_convertible_v<iterator_category_t<I>, Tag>>> = true;
    
    /// Increment (decrement for negative n) i |n| times or until i == bound,
    /// whichever comes first. Returns n - the difference between i's final position
    /// and its initial position. (Note: "advance" has overloads with this behavior
    /// in the Ranges TS.)
    template<class I>
    constexpr difference_type_t<I> bounded_advance(
        I& i, difference_type_t<I> n, I const bound)
    {
        if constexpr (is_category<I, std::bidirectional_iterator_tag>) {
            for (; n < 0 && i != bound; ++n, void(--i)) {
                ;
            }
        }
    
        for(; n > 0 && i != bound; --n, void(++i)) {
            ;
        }
    
        return n;
    }
    
    template<class ForwardIt>
    ForwardIt shift_left(ForwardIt first, ForwardIt last, difference_type_t<ForwardIt> n)
    {
        if (n <= 0) {
            return last;
        }
    
        auto mid = first;
        if (::bounded_advance(mid, n, last)) {
            return first;
        }
    
        return std::move(std::move(mid), std::move(last), std::move(first));
    }
    
    template<class ForwardIt>
    ForwardIt shift_right(ForwardIt first, ForwardIt last, difference_type_t<ForwardIt> n)
    {
        if (n <= 0) {
            return first;
        }
    
        if constexpr (is_category<ForwardIt, std::bidirectional_iterator_tag>) {
            auto mid = last;
            if (::bounded_advance(mid, -n, first)) {
                return last;
            }
            return std::move_backward(std::move(first), std::move(mid), std::move(last));
        } else {
            auto result = first;
            if (::bounded_advance(result, n, last)) {
                return last;
            }
    
            // Invariant: next(first, n) == result
            // Invariant: next(trail, n) == lead
    
            auto lead = result;
            auto trail = first;
    
            for (; trail != result; ++lead, void(++trail)) {
                if (lead == last) {
                    // The range looks like:
                    //
                    //   |-- (n - k) elements --|-- k elements --|-- (n - k) elements --|
                    //   ^-first          trail-^                ^-result          last-^
                    //
                    // Note that distance(first, trail) == distance(result, last)
                    std::move(std::move(first), std::move(trail), std::move(result));
                    return result;
                }
            }
    
            for (;;) {
                for (auto mid = first; mid != result; ++lead, void(++trail), ++mid) {
                    if (lead == last) {
                        // The range looks like:
                        //
                        //   |-- (n - k) elements --|-- k elements --|-- ... --|-- n elements --|
                        //   ^-first            mid-^         result-^         ^-trail     last-^
                        //
                        trail = std::move(mid, result, std::move(trail));
                        std::move(std::move(first), std::move(mid), std::move(trail));
                        return result;
                    }
                    std::iter_swap(mid, trail);
                }
            }
        }
    }