Search code examples
c++copy-constructorc++03stable-sort

How to stable_sort without copying?


Why does stable_sort need a copy constructor? (swap should suffice, right?)
Or rather, how do I stable_sort a range without copying any elements?

#include <algorithm>

class Person
{
    Person(Person const &);  // Disable copying
public:
    Person() : age(0) { }
    int age;
    void swap(Person &other) { using std::swap; swap(this->age, other.age); }
    friend void swap(Person &a, Person &b) { a.swap(b); }
    bool operator <(Person const &other) const { return this->age < other.age; }
};

int main()
{
    static size_t const n = 10;
    Person people[n];
    std::stable_sort(people, people + n);
}

Solution

  • Expanding upon the discussion in the OP, and because I found it interesting, here's a solution which uses only swap to sort the original vector (by using a pointer wrapper to sort indices).

    Edit: this is the solution v2, which swaps in-place.

    Edit (by OP): An STL-friendly version which doesn't require C++11.

    template<class Pred>
    struct swapping_stable_sort_pred
    {
        Pred pred;
        swapping_stable_sort_pred(Pred const &pred) : pred(pred) { }
    
        template<class It>
        bool operator()(
            std::pair<It, typename std::iterator_traits<It>::difference_type> const &a,
            std::pair<It, typename std::iterator_traits<It>::difference_type> const &b) const
        {
            bool less = this->pred(*a.first, *b.first);
            if (!less)
            {
                bool const greater = this->pred(*b.first, *a.first);
                if (!greater) { less = a.second < b.second; }
            }
            return less;
        }
    };
    
    template<class It, class Pred>
    void swapping_stable_sort(It const begin, It const end, Pred const pred)
    {
        typedef std::pair<It, typename std::iterator_traits<It>::difference_type> Pair;
        std::vector<Pair> vp;
        vp.reserve(static_cast<size_t>(std::distance(begin, end)));
        for (It it = begin; it != end; ++it)
        { vp.push_back(std::make_pair(it, std::distance(begin, it))); }
        std::sort(vp.begin(), vp.end(), swapping_stable_sort_pred<Pred>(pred));
        std::vector<Pair *> vip(vp.size());
        for (size_t i = 0; i < vp.size(); i++)
        { vip[static_cast<size_t>(vp[i].second)] = &vp[i]; }
    
        for (size_t i = 0; i + 1 < vp.size(); i++)
        {
            typename std::iterator_traits<It>::difference_type &j = vp[i].second;
            using std::swap;
            swap(*(begin + static_cast<ptrdiff_t>(i)), *(begin + j));
            swap(j, vip[i]->second);
            swap(vip[j], vip[vip[j]->second]);
        }
    }
    
    template<class It>
    void swapping_stable_sort(It const begin, It const end)
    { return swapping_stable_sort(begin, end, std::less<typename std::iterator_traits<It>::value_type>()); }