Pourquoi le swap est-il appelé par std :: sort uniquement si mon conteneur contient plus de 32 éléments?

13

Bonjour j'ai une question simple:

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

}

Avec plus de 32 éléments, le tri appelle swap. Avec 32 éléments ou moins, les éléments sont toujours triés mais swapne sont pas appelés.

  • J'utilise MSVC ++ 2019 sur x64.
  • Quand est-il swapappelé et quand ne l'est-il pas et pourquoi? Je vous remercie!
  • Je n'ai pas utilisé swapdans la copie-affectation uniquement pour distinguer l'appel à la sorte de l'opérateur de copie-affectation.
Maestro
la source
6
std::sortrecourt au tri par insertion si le nombre d'éléments est inférieur ou égal à 32 et utilise le tri rapide dans le cas contraire.
Evg
@Evg Est-ce une exigence ou une explication pour ce contexte particulier?
François Andrieux
2
@ FrançoisAndrieux, c'est un détail d'implémentation de la bibliothèque standard de Microsoft. Je suppose que c'est la raison du comportement observé par OP. J'examine actuellement le code source pour obtenir plus de détails.
Evg
1
La partie pertinente de la source est: while (_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal)_ISORT_MAXest donnée la valeur de 32. Ligne 3447 d' <algorithm>utilisation de VS 16.5.0
ChrisMM
Aucun véritable tri rapide n'est utilisé dans les bibliothèques standard modernes dans toutes les langues. Tous utilisent des versions mixtes modifiées qui ne sont un tri rapide que lorsque le nombre d'éléments est suffisamment important. Par exemple, Java et Python utilisent Timsort tandis que le framework .NET et la bibliothèque C ++ de GCC utilisent Introsort . libstdc ++ et libc ++ utilisent également le tri par insertion pour les séquences courtes. Voir Quels algorithmes sont utilisés dans C ++ 11 std :: sort dans différentes implémentations STL?
phuclv

Réponses:

14

L' std::sortimplémentation de Microsoft ressemble à ceci:

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
}

Lorsque la plage à trier comporte 32 éléments ou moins, Sortutilise le tri par insertion. Le tri par insertion n'utilise pas swapdans son implémentation . Sinon, le tri rapide par division et conquête est utilisé. Dans la mise en œuvre, il appelle iter_swap(à l'intérieur Unguarded_partition), qui à son tour appelle swap:

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

Ce sont tous des détails de mise en œuvre. Ils varient d'une implémentation de bibliothèque standard à une autre.

Evg
la source
1
libcxx utilise le tri par insertion pour les séquences de longueur inférieure à 6 ou 30 selon le type. libstd ++ fait cela pour des séquences de 16 éléments ou moins. Quels algorithmes sont utilisés dans C ++ 11 std :: sort dans différentes implémentations STL?
phuclv