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

时间:2019-04-09 13:44:36

标签: c++ sorting c++11 pointers

已经有sorting vector of 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';
}

但是,似乎只对向量进行了排序,如输出所示:

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

我认为原因是std::sort()在正确比较的情况下仅分配了地址而不是值。怎么了为什么我不能对指向指针的向量进行排序?

下一部分是TL,DR,因为它显示了我解决此问题的方法。一个容易完成的任务使自己显得非常复杂。 @Bathsheba's answer指出不可能。因此,下一部分最初被认为是我的尝试的呈现,现在可能被认为是为什么不可能的原因。


我的想法是制作一个指针类包装器,以提供我自己的构造函数和赋值运算符。如果容器的尺寸很小(在我的系统上为std::sort()),<= 32的行为会有所不同,但是在两种情况下,分配和移动都会发生-正如_Insertion_sort_unchecked(来自{ {1}})功能显示。

({<algorithm> == _BidItstd::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进行调用时调用了哪些构造函数/赋值运算符:

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()进行排序,但对vecab进行排序

最后一点很有意义,因为因为仅调用了移动功能,所以从未调用过副本分配运算符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()类中的初始m_ptr始终为this->,因此我不想取消引用。
  • nullptr从不使用std::sort(),而只是从各处使用std::swap()。 (如std::move()所见)。

2 个答案:

答案 0 :(得分:1)

您需要滚动自己的排序功能才能做到这一点。

回调lambda用于评估顺序,但是您需要调整用于实际交换元素的部分:而C ++标准库sort不支持您这样做。

幸运的是,没有任何花哨的快速排序(例如预先随机化)出现在几十行中,因此要完成该任务并不是特别繁重的任务。

答案 1 :(得分:0)

只需保留指针原始向量的副本,然后将排序后的值复制到以下位置即可:

            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