[英]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()
只调用移动函数。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:
我看到了两种可能的解决方案:
swap()
for the classswap()
but both don't work.但两者都不起作用。
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.