Shift_right () dự định được triển khai trong C ++ 20 như thế nào?


9

Trong C ++ 20, <algorithm>tiêu đề đạt được hai thuật toán mới: shift_left()shift_right() . Cả hai đều chấp nhận bất kỳ LegacyForwardIterator. Đối với shift_left(), nó được chỉ định rằng "các động tác được thực hiện theo thứ tự tăng dần ibắt đầu từ ​0"; cho shift_right(), nó được chỉ định rằng "nếu ForwardItđáp ứng các yêu cầu LegacyBidirectionalIterator, thì các bước di chuyển được thực hiện theo thứ tự giảm dần ibắt đầu từ last - first - n - 1".

Tôi có thể nghĩ ra một cách hợp lý dễ thực hiện shift_left():

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);
}

Nếu ForwardItđáp ứng các yêu cầu của LegacyBidirectionalIterator, tôi có thể thấy điều đó shift_right()có thể được thực hiện theo cách rất giống như shift_left(). Tuy nhiên, không rõ ràng về cách người ta có thể thực hiện shift_right()cho các trình vòng lặp chuyển tiếp không hai chiều.

Tôi đã tìm ra một thuật toán sử dụng không gian ở [first, first+n)dạng không gian đầu để hoán đổi các phần tử, nhưng có vẻ lãng phí hơn một chút so với thuật toán shift_left()ở trê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;
}

Sẽ có một cách tốt hơn hoặc "dự định" để thực hiện shift_right()?


Triển khai thay vì sử dụng std::movethay vì std::copy...
Aconcagua

@Aconcagua Rất tiếc, vâng, tôi sẽ chỉnh sửa câu hỏi.
Bernard

Câu trả lời:


6

Đây là triển khai mẫu cho các ca: https://github.com/danra/shift_projection/blob/master/shift_projection.h

Từ tài liệu đề xuất: http://www.open-std.org/jtc1/sc22/wg21/docs/ con / 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);
            }
        }
    }
}

3
Tôi tự hỏi tại sao void(++trail)...
YSC

@YSC bảo vệ chống lại cảnh báo "kết quả bị loại bỏ" quá nhiệt tình
Caleth

@vll Đó là những gì tôi nghĩ.
YSC

5
@YSC Có lẽ để bảo vệ chống lại các toán tử dấu phẩy quá tải mà không được gọi là.
quả óc chó

@walnut ho bạn có thể đúng!
YSC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.