简体   繁体   English

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

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

There is already the post sorting vector of pointers but this is not about a vector of pointers but rather about a vector of referencing pointers.已经有指针的后排序向量,但这不是指针向量,而是引用指针向量。

3 integer are put into a std::vector<int*> which is than sorted according to the values behind the pointers.将 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';
}

however, it seems that only the vector was sorted as the output shows:然而,似乎只有向量被排序,如输出所示:

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

I think the reason is that std::sort() , while comparing correctly, simply assigns adresses instead of the values.我认为原因是std::sort()在正确比较时,只是分配地址而不是值。 What is wrong here?这里有什么问题? Why can't I sort this vector of pointed-to values?为什么我不能对这个指向值的向量进行排序?

The next part is rather TL,DR as it shows my approach to solve this.下一部分是 TL,DR,因为它展示了我解决这个问题的方法。 An easy task that reveals itself to be rather frustratingly complicated.一项简单的任务,却显示出相当令人沮丧的复杂性。 @Bathsheba's answer points out that it is not possible . @Bathsheba 的回答指出这是不可能的 So the next parts, initially considered to be the presentation of my attempt, might now be considered to be the reason why it is not possible.所以接下来的部分,最初被认为是我尝试的表现,现在可能认为是它之所以是不可能的。


My idea is to make a pointer class wrapper to provide my own contructors and assignement operators.我的想法是制作一个指针类包装器来提供我自己的构造函数和赋值运算符。 std::sort() behaves differently if the size of the container is small ( <= 32 on my system) but in both cases there are assignements and moves happening - as this small snippet from the _Insertion_sort_unchecked (from <algorithm> ) function shows.如果容器的大小很小(在我的系统上为<= 32 std::sort()行为会有所不同,但在这两种情况下,都会发生分配和移动 - 正如来自_Insertion_sort_unchecked (来自<algorithm> )函数的这个小片段所示.

( _BidIt == std::vector<int*>::iterator and _Iter_value_t<_BidIt> == int* ) ( _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);

Let's make a class assignement_pointer that behaves like a pointer except that it assigns the values instead of the adresses.让我们创建一个类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;
};

As you can see there are also temporary std::cout 's to see which constructors / assignement operators were called while going through std::sort() in the main:正如您所看到的,还有临时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';

giving the output:给出输出:

<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() calls only move functions. std::sort()只调用移动函数。
  • again, vec is sorted but not a , b , c再次, vec已排序但未排序a , b , c

the last point makes sense, because since only move functions are called the copy assignement operator assignement_pointer& operator=(const assignement_pointer& other);最后一点是有道理的,因为只有移动函数被称为复制赋值运算符assignement_pointer& operator=(const assignement_pointer& other); (which does the value assignement) is never called. (进行值分配)永远不会被调用。 The unnecessary copy constructor and assignement operator can be removed:可以删除不必要的复制构造函数和赋值运算符:

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;
};

Now std::sort() inner processes are rather complicated but in the end it comes down to failing at an operation like std::swap() :现在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';
}

and as this output shows:正如此输出所示:

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

it's that sitation that only the pointers are switched but not the original variables.就是只切换指针而不切换原始变量的情况。 std::swap is basically std::swap基本上是

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

explaining the解释

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

the move assignement operator simply swaps the pointers so creating a temporary variable doesn't do anything.移动赋值运算符只是交换指针,因此创建临时变量不会做任何事情。 I see two possible solutions to this:我看到了两种可能的解决方案:

  • make the move assignement operator not swap pointers but rather values.使移动赋值运算符不交换指针而是交换值。
  • implement my own swap() for the class为班级实现我自己的swap()

but both don't work.但两者都不起作用。

  • the move assignement operator can't swap values because the initial m_ptr from this-> class is always nullptr and I would rather not dereference this.移动赋值运算符不能交换值,因为来自this->类的初始m_ptr始终为nullptr ,我宁愿不取消引用它。
  • std::sort() never uses std::swap() but instead just std::move() s from all over the place. std::sort()从不使用std::swap()而是只使用来自各地的std::move() (as already partially seen by _Insertion_sort_unchecked ). (正如_Insertion_sort_unchecked已经部分看到的_Insertion_sort_unchecked )。

You'll need to roll your own sort function to do this.您需要推出自己的排序功能来执行此操作。

The callback lambda is used to evaluate the ordering, but you need to tweak the part that does the actual exchange of elements: and the C++ standard library sort does not support your doing that.回调 lambda 用于评估排序,但您需要调整进行实际元素交换的部分:而 C++ 标准库sort不支持您这样做。

Fortunately a quick sort without any bells and whistles (such as pre-randomisation) comes out in a few tens of lines, so this is not a particularly onerous task to complete.幸运的是,没有任何花里胡哨的快速排序(例如预随机化)出现在几十行中,所以这不是一项特别繁重的任务。

Just retain a copy of the original vector of pointers and copy the sorted values over:只需保留原始指针向量的副本并将排序后的值复制到:

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

Then after you print a,b,c:然后在打印 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';

Output:输出:

abc = 1, 2, 3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM