我可以在 std::list 中移动元素而不会使迭代器或引用无效,但是如何?

3 c++ linked-list stdlist

来自cppreference 文章std::list

在列表内或跨多个列表添加、删除和移动元素不会使迭代器或引用无效。只有当相应的元素被删除时,迭代器才会失效。

事实上,排序元素时就是这种情况。来自cppreference 文章std::list::sort

该函数的不同之处还在于std::sort它不需要列表的元素类型是可交换的,保留所有迭代器的值,并执行稳定的排序。

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

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

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

现在it指向1jt指向2。我可以重新排列这个列表,使之2出现在之前1,但it仍指向1吗?

我可以:

std::swap(*it, *jt);
Run Code Online (Sandbox Code Playgroud)

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

鉴于 cppreference 的前面引用,我想应该有可能实现我想要实现的目标;但是怎么样?

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

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

现在it指向2jt指向1

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

l.sort();
Run Code Online (Sandbox Code Playgroud)

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

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

Hol*_*Cat 5

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

按照@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 << ' ';
Run Code Online (Sandbox Code Playgroud)