簡體   English   中英

如何使用 std::sort() 對指向值的向量進行排序?

[英]How can can I sort a vector of pointed-to values using std::sort()?

已經有指針的后排序向量,但這不是指針向量,而是引用指針向量。

將 3 個整數放入std::vector<int*> ,然后根據指針后面的值對其進行排序。

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    int a = 3;
    int b = 2;
    int c = 1;

    std::vector<int*> vec;
    vec.emplace_back(&a);
    vec.emplace_back(&b);
    vec.emplace_back(&c);

    std::sort(vec.begin(), vec.end(), [](const int* a, const int* b) {
        return *a < *b;
    });

    std::cout << "vec = " << *vec[0] << ", " << *vec[1] << ", " << *vec[2] << '\n';
    std::cout << "abc = " << a << ", " << b << ", " << c << '\n';
}

然而,似乎只有向量被排序,如輸出所示:

vec = 1, 2, 3 
abc = 3, 2, 1 

我認為原因是std::sort()在正確比較時,只是分配地址而不是值。 這里有什么問題? 為什么我不能對這個指向值的向量進行排序?

下一部分是 TL,DR,因為它展示了我解決這個問題的方法。 一項簡單的任務,卻顯示出相當令人沮喪的復雜性。 @Bathsheba 的回答指出這是不可能的 所以接下來的部分,最初被認為是我嘗試的表現,現在可能認為是它之所以是不可能的。


我的想法是制作一個指針類包裝器來提供我自己的構造函數和賦值運算符。 如果容器的大小很小(在我的系統上為<= 32 std::sort()行為會有所不同,但在這兩種情況下,都會發生分配和移動 - 正如來自_Insertion_sort_unchecked (來自<algorithm> )函數的這個小片段所示.

( _BidIt == std::vector<int*>::iterator_Iter_value_t<_BidIt> == int* )

_BidIt _Insertion_sort_unchecked(_BidIt _First, const _BidIt _Last, _Pr _Pred)
    {   // insertion sort [_First, _Last), using _Pred
    if (_First != _Last)
        {
        for (_BidIt _Next = _First; ++_Next != _Last; )
            {   // order next element
            _BidIt _Next1 = _Next;
            _Iter_value_t<_BidIt> _Val = _STD move(*_Next);

            if (_DEBUG_LT_PRED(_Pred, _Val, *_First))
                {   // found new earliest element, move to front
                _Move_backward_unchecked(_First, _Next, ++_Next1);
                *_First = _STD move(_Val);

讓我們創建一個類assignement_pointer ,它的行為類似於指針,只是它分配的是值而不是地址。

template<typename T>
class assignement_pointer {
public:
    assignement_pointer(T& value) {
        this->m_ptr = &value;
        std::cout << "<T>& constructor\n";
    }
    assignement_pointer(const assignement_pointer& other) {
        this->m_ptr = other.m_ptr;
        std::cout << "copy constructor\n";
    }
    assignement_pointer(assignement_pointer&& other) {
        std::cout << "move assignement constructor >> into >> ";
        *this = std::move(other);
    }
    assignement_pointer& operator=(const assignement_pointer& other) {
        *this->m_ptr = *other.m_ptr;
        std::cout << "copy assignement operator\n";
        return *this;
    }
    assignement_pointer& operator=(assignement_pointer&& other) {
        std::swap(this->m_ptr, other.m_ptr);
        std::cout << "move assignement operator\n";
        return *this;
    }
    T& operator*() {
        return *this->m_ptr;
    }
    const T& operator*() const {
        return *this->m_ptr;
    }
private:
    T* m_ptr;
};

正如您所看到的,還有臨時std::cout可以查看在 main 中通過std::sort()調用了哪些構造函數/賦值運算符:

    ///...
    std::vector<assignement_pointer<int>> vec;
    vec.reserve(3);
    vec.emplace_back(assignement_pointer(a));
    vec.emplace_back(assignement_pointer(b));
    vec.emplace_back(assignement_pointer(c));

    std::cout << "\nsort()\n";
    std::sort(vec.begin(), vec.end(), [](const assignement_pointer<int>& a, const assignement_pointer<int>& b) {
        return *a < *b;
    });

    std::cout << "\nvec = " << *vec[0] << ", " << *vec[1] << ", " << *vec[2] << '\n';
    std::cout << "abc = " << a << ", " << b << ", " << c << '\n';

給出輸出:

<T>& constructor
move assignement constructor >> into >> move assignement operator
<T>& constructor
move assignement constructor >> into >> move assignement operator
<T>& constructor
move assignement constructor >> into >> move assignement operator

sort()
move assignement constructor >> into >> move assignement operator
move assignement operator
move assignement operator
move assignement constructor >> into >> move assignement operator
move assignement operator
move assignement operator
move assignement operator

vec = 1, 2, 3
abc = 3, 2, 1
  • std::sort()只調用移動函數。
  • 再次, vec已排序但未排序a , b , c

最后一點是有道理的,因為只有移動函數被稱為復制賦值運算符assignement_pointer& operator=(const assignement_pointer& other); (進行值分配)永遠不會被調用。 可以刪除不必要的復制構造函數和賦值運算符:

template<typename T>
class assignement_pointer {
public:
    assignement_pointer(T& value) {
        this->m_ptr = &value;
    }
    assignement_pointer(const assignement_pointer& other) = delete;
    assignement_pointer& operator=(const assignement_pointer& other) = delete;
    assignement_pointer(assignement_pointer&& other) {
        std::cout << "move assignement constructor >> into >> ";
        *this = std::move(other);
    }
    assignement_pointer& operator=(assignement_pointer&& other) {
        std::swap(this->m_ptr, other.m_ptr);
        std::cout << "move assignement operator\n";
        return *this;
    }
    T& operator*() {
        return *this->m_ptr;
    }
    const T& operator*() const {
        return *this->m_ptr;
    }
private:
    T* m_ptr;
};

現在std::sort()內部進程相當復雜,但最終歸結為在像std::swap()這樣的操作中失敗:

 int main() {
    int a = 3;
    int b = 2;

    std::vector<assignement_pointer<int>> vec;
    vec.reserve(2); //to avoid re-allocations
    vec.emplace_back(assignement_pointer(a));
    vec.emplace_back(assignement_pointer(b));

    std::cout << "swap()\n";

    assignement_pointer<int> ptr_a{ a };
    assignement_pointer<int> ptr_b{ b };

    std::swap(ptr_a, ptr_b);

    std::cout << "\nptrs = " << *ptr_a << ", " << *ptr_b << '\n';
    std::cout << "a, b = " << a << ", " << b << '\n';
}

正如此輸出所示:

move assignement constructor >> into >> move assignement operator
move assignement constructor >> into >> move assignement operator
swap()
move assignement constructor >> into >> move assignement operator
move assignement operator
move assignement operator

ptrs = 2, 3
a, b = 3, 2

就是只切換指針而不切換原始變量的情況。 std::swap基本上是

_Ty _Tmp = _STD move(_Left);
_Left = _STD move(_Right);
_Right = _STD move(_Tmp);

解釋

move assignement constructor >> into >> move assignement operator
move assignement operator
move assignement operator

移動賦值運算符只是交換指針,因此創建臨時變量不會做任何事情。 我看到了兩種可能的解決方案:

  • 使移動賦值運算符不交換指針而是交換值。
  • 為班級實現我自己的swap()

但兩者都不起作用。

  • 移動賦值運算符不能交換值,因為來自this->類的初始m_ptr始終為nullptr ,我寧願不取消引用它。
  • std::sort()從不使用std::swap()而是只使用來自各地的std::move() (正如_Insertion_sort_unchecked已經部分看到的_Insertion_sort_unchecked )。

您需要推出自己的排序功能來執行此操作。

回調 lambda 用於評估排序,但您需要調整進行實際元素交換的部分:而 C++ 標准庫sort不支持您這樣做。

幸運的是,沒有任何花里胡哨的快速排序(例如預隨機化)出現在幾十行中,所以這不是一項特別繁重的任務。

只需保留原始指針向量的副本並將排序后的值復制到:

            std::vector<int*> original = vec;  // Do this before the std::sort

然后在打印 a,b,c 之后:

            std::vector<int> xfer;
            for (auto ptr : vec) {
                xfer.push_back(*ptr);
            }
            auto it = std::begin(xfer);
            for (auto ptr : original) {
                *ptr = *it++;
            }
            std::cout << "abc = " << a << ", " << b << ", " << c << '\n';

輸出:

abc = 1, 2, 3

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM