コンテナに32を超える要素がある場合にのみ、なぜstd :: sortによってswapが呼び出されるのですか?


13

こんにちは私は簡単な質問があります:

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

}

32要素を超える場合、並べ替えはを呼び出しますswap。32要素以下の場合でも、要素はソートされますがswap呼び出されません。

  • x64でMSVC ++ 2019を使用しています。
  • いつswap呼び出され、いつ呼び出されないのか、その理由は?ありがとうございました!
  • 私はswap、コピー代入でそれを呼び出すことと、コピー代入演算子からのソートとを区別するためだけに使用していません。

6
std::sort要素の数が32以下の場合は挿入ソートを使用し、それ以外の場合はクイックソートを使用します。
19

@Evgそれは要件ですか、それともこの特定のコンテキストの説明ですか?
フランソワアンドリュー

2
@FrançoisAndrieux、これはMicrosoft標準ライブラリの実装の詳細です。これがOPによって観察された動作の理由であると私は推測しています。現在、ソースコードを調べて詳細を調べています。
19

1
ソースの関連部分は次のとおりです。while (_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal)ここで_ISORT_MAX、32の値が指定されています<algorithm>。VS16.5.0 を使用する3447行目
ChrisMM

どの言語の最新の標準ライブラリでも、実際のクイックソートは使用されていません。要素の数が十分に多い場合にのみクイックソートである変更された混合バージョンをすべて使用します。JavaやPythonの使用たとえばTimsortは、 .NETフレームワークとGCCのC ++ライブラリを使用していますがイントロソート。libstdc ++およびlibc ++も、短いシーケンスに挿入ソートを使用します。さまざまなSTL実装のC ++ 11 std :: sortで使用されているアルゴリズムを
phuclv

回答:


14

Microsoftのstd::sort実装は次のようになります。

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
}

ソートする範囲の要素が32個以下の場合、Sort挿入ソートを使用します。挿入ソートはswapその実装では使用しません。それ以外の場合は、分割統治クイックソートが使用されます。で実装それが呼び出すiter_swap(内側Unguarded_partition)、そのターンの呼び出しでswap

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

これらはすべて実装の詳細です。それらは、標準ライブラリの実装によって異なります。


1
libcxxは、タイプに応じて6または30未満の長さのシーケンスに挿入ソートを使用します。libstd ++は、16要素以下のシーケンスに対してそれを行います。さまざまなSTL実装のC ++ 11 std :: sortで使用されているアルゴリズムは何ですか?
phuclv
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.