Tại sao hoán đổi được gọi bởi std :: sort chỉ khi container của tôi có nhiều hơn 32 phần tử?


13

Xin chào tôi có một câu hỏi đơn giản:

class A 
{
public:
    A(int);
    A(const A&);
    A& operator=(const A&);
    ~A();
private:
    int* ptr_;

    friend bool operator<(const A&, const A&);
    friend void swap(A&, A&);
};

A::A(int x) : 
    ptr_(new int(x))
{}

A::A(const A& rhs) :
    ptr_(rhs.ptr_ ? new int(*rhs.ptr_) : nullptr)
{}

A& A::operator = (const A & rhs)
{
    int* tmp = rhs.ptr_ ? new int(*rhs.ptr_) : nullptr;
    delete ptr_;
    ptr_ = tmp;

    return *this;
}

A::~A()
{
    delete ptr_;
}

bool operator<(const A& lhs, const A& rhs)
{
    cout << "operator<(const A&, const A&)" << endl;
    return *lhs.ptr_ < *rhs.ptr_;
}

void swap(A& lhs, A& rhs)
{
    cout << "swap(A&, A&)" << endl;
    using std::swap;
    swap(lhs.ptr_, rhs.ptr_);
}

int main()
{

    std::vector<A> v{ 33,32,31,30,29,28,27,26,25,24,23,22, 21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5, 4,3,2,1 };
    std::sort(v.begin(), v.end());

}

Với hơn 32 yếu tố, các cuộc gọi sắp xếp swap. Với 32 phần tử trở xuống, các phần tử vẫn được sắp xếp nhưng swapkhông được gọi.

  • Tôi đang sử dụng MSVC ++ 2019 trên x64.
  • Khi nào được swapgọi và khi nào nó không và tại sao? Cảm ơn bạn!
  • Tôi đã không sử dụng swaptrong chỉ định sao chép để phân biệt giữa cuộc gọi đến cuộc gọi từ nhà điều hành chuyển nhượng sao chép.

6
std::sortsử dụng sắp xếp chèn nếu số lượng phần tử là 32 hoặc ít hơn và sử dụng sắp xếp nhanh khác.
Evg

@Evg Đó có phải là một yêu cầu hay đó là một lời giải thích cho bối cảnh cụ thể này?
François Andrieux

2
@ FrançoisAndrieux, đây là chi tiết triển khai của thư viện chuẩn Microsoft. Tôi đoán rằng đây là lý do của hành vi được quan sát bởi OP. Tôi hiện đang xem xét mã nguồn để biết thêm chi tiết.
Evg

1
Phần có liên quan của nguồn là: while (_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal)nơi _ISORT_MAXđược cho giá trị là 32. Dòng 3447 <algorithm>sử dụng VS 16.5.0
ChrisMM

Không có quicksort thực sự được sử dụng trong bất kỳ thư viện tiêu chuẩn hiện đại trong bất kỳ ngôn ngữ. Tất cả đều sử dụng các phiên bản hỗn hợp đã sửa đổi, chỉ là bản tóm tắt khi số lượng phần tử đủ lớn. Ví dụ: Java và Python sử dụng Timsort trong khi .NET framework và thư viện C ++ của GCC sử dụng Introsort . libstdc ++ và libc ++ cũng sử dụng sắp xếp chèn cho các chuỗi ngắn. Xem những thuật toán nào được sử dụng trong C ++ 11 std :: sort trong các triển khai STL khác nhau?
phuclv

Câu trả lời:


14

Microsoft std::sortthực hiện trông như thế này:

const int ISORT_MAX = 32;  // maximum size for insertion sort

template<class RanIt, class Diff, class Pr>
void Sort(RanIt First, RanIt Last, Diff Ideal, Pr Pred)
{
    Diff Count;
    for (; ISORT_MAX < (Count = Last - First) && 0 < Ideal; )
    {   // divide and conquer by quicksort
        pair<RanIt, RanIt> Mid = Unguarded_partition(First, Last, Pred);

        // ...
    }

    if (ISORT_MAX < Count)
    {   // heap sort if too many divisions
        Make_heap(First, Last, Pred);
        Sort_heap(First, Last, Pred);
    }
    else if (1 < Count)
        Insertion_sort(First, Last, Pred);  // small
}

Khi phạm vi được sắp xếp có 32 phần tử trở xuống, Sortsử dụng sắp xếp chèn. Sắp xếp chèn không sử dụng swaptrong thực hiện của nó . Mặt khác, sắp xếp nhanh chóng phân chia và chinh phục được sử dụng. Trong thực hiện nó gọi iter_swap(bên trong Unguarded_partition), trong các cuộc gọi lần lượt swap:

template<class FwdIt1, class FwdIt2>
void iter_swap(FwdIt1 Left, FwdIt2 Right)
{   // swap *Left and *Right
    swap(*Left, *Right);
}

Tất cả đều là chi tiết thực hiện. Chúng thay đổi từ một triển khai thư viện tiêu chuẩn khác.


1
libcxx sử dụng sắp xếp chèn cho các chuỗi có độ dài nhỏ hơn 6 hoặc 30 tùy thuộc vào loại. libstd ++ thực hiện điều đó cho các chuỗi từ 16 phần tử trở xuống. Những thuật toán nào được sử dụng trong C ++ 11 std :: sort trong các triển khai STL khác nhau?
phuclv
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.