我可以在 std::list 内移动元素而不会使迭代器或引用失效,但是怎么做呢?

I can move elements within the std::list without invalidating iterators nor references, but how?

来自cppreference article on std::list:

Addition, removal and moving the elements within the list or across several lists does not invalidate the iterators or references. An iterator is invalidated only when the corresponding element is deleted.

事实上,在对元素进行排序时就是这种情况。来自 cppreference article on std::list::sort:

This function also differs from std::sort in that it does not require the element type of the list to be swappable, preserves the values of all iterators, and performs a stable sort.

但是如何在保留所有迭代器的值的同时任意交换两个元素的位置?

例如,假设我有一个列表:

std::list<int> l({1, 2, 3, 4});
auto it = l.begin(), jt = ++l.begin();

现在 it 指向 1jt 指向 2。我可以重新排序此列表,以便 2 排在 1 之前,但 it 仍指向 1 吗?

我能做到:

std::swap(*it, *jt);

但是,虽然 2 会出现在 1 之前,但我不会保留迭代器的值,因为显然 it 将指向 2

鉴于cppreference的forementend引用,我想应该可以实现我想要实现的目标;但是如何?

编辑:为了清楚起见:再举一个例子:

std::list<int> l({2, 1, 3, 4, 5});
auto it = l.begin(), jt = ++l.begin();

现在 it 指向 2jt 指向 1

std::list::sort 具有我正在寻找的语义:

l.sort();

现在列表的顺序是:1, 2, 3, 4, 5,但是 it 仍然指向 2jt 仍然指向 1

另一方面,std::swap(it, jt)std::swap(*it, *jt) 都没有我想要的语义。调用它们中的任何一个都会使 it 指向 1jt 指向 2。 (ideone proof)

你们正在交换价值观。不是迭代器值。所以迭代器保持不变,而它们指向的值发生变化(交换)。

将迭代器视为指针,至少在这方面是这样。

所以你会做些什么来交换迭代器和值就是这样:

std::swap(*it, *jt);
std::swap(it, jt);

But how may I arbitrarily swap the positions of two elements while preserving the values of all iterators?

按照@cpplearner 的建议,使用.splice()

它可以对单个元素或范围进行操作。它还可以跨列表传输元素。

下面是一个演示如何移动单个元素的简单示例。

std::list<int> list{1,2,3,4,5};

// This element will be moved
auto source = std::find(list.begin(), list.end(), 4);

// It will be inserted before this element
auto destination = std::find(list.begin(), list.end(), 2);

list.splice(destination, list, source);
// ^                      ^
// |                      `- A list to move from
// `- A list to move to   

// Prints `1 4 2 3 5`.
for (int it : list) std::cout << it << ' ';