Уже существует вектор сортировки указателей , но речь идет не о векторе указателей, а о векторе ссылочных указателей.
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, поскольку она показывает мой подход к решению этой проблемы. Простая задача, которая оказывается довольно сложной. @ Ответ Вирсавии указывает, что это невозможно . Поэтому следующие части, которые первоначально рассматривались как представление моей попытки, теперь могут рассматриваться как причина , почему это невозможно.
Моя идея состоит в том, чтобы создать класс-оболочку указателя для моих собственных конструкторов и операторов присваивания. std::sort()
ведет себя по-разному, если размер контейнера невелик (<= 32
в моей системе), но в обоих случаях происходят назначения и перемещения - как показывает этот небольшой фрагмент из функции _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
, чтобы увидеть, какие конструкторы / операторы присваивания были вызваны при прохождении 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()
для класса
но оба не работают.
- оператор присваивания перемещения не может поменять значения, потому что начальный
m_ptr
из this->
класса всегда nullptr
, и я бы не стал разыменовывать это.
std::sort()
никогда не использует std::swap()
, а вместо этого просто std::move()
с повсюду. (как уже частично замечено _Insertion_sort_unchecked
).