C++11 std::list iterator invalidation after splicing
list
iterators point to elements. By splicing a range of elements, you've changed which container those iterators point into. The element is no longer an element of A
; it is an element of B
. Iterators are preserved across splice
ing, so p
and p_copy
will still point to those elements.
They're just iterators into different containers now.
std::list::splice invalidates iterators. Rationale?
In C++11 splice
does not invalidate the iterators, but make them refer to the appropriate elements in the *this
container. This is all described in 23.3.5.5.
STL list::splice - iterator validity
It's only a guess, but they might have written that to imply that it
is now "invalid" in the sense that it is no longer a valid iterator of mylist1
, but instead becomes a valid iterator of mylist2
.
But still, and I guess you already knew that, it is a valid iterator, so the wording is misleading. You need to be careful, though, as it means that after the second splice-operation, for example, you can no longer do:
std::distance( mylist1.begin(), it );
but need to use
std::distance( mylist2.begin(), it );
as the first would be illegal.
The standard clearly defines it that way in:
23.3.5.5 list operations [list.ops]
void splice(const_iterator position, list& x, const_iterator i);
void splice(const_iterator position, list&& x, const_iterator i);
7 Effects: Inserts an element pointed to by
i
from listx
beforeposition
and removes the element fromx
. The result is unchanged ifposition == i
orposition == ++i
. Pointers and references to*i
continue to refer to this same element but as a member of*this
. Iterators to*i
(includingi
itself) continue to refer to the same element, but now behave as iterators into*this
, not intox
.
So, if your compiler/STL invalidates the iterator, this is clearly a bug.
std::list - are the iterators invalidated on move?
For containers in general, only swap
guarantees that iterators remain valid (and point into the swapped containers).
For std::list
, the special member function splice()
guarantees that iterators retain their expected meaning.
In general, constructing a container from an rvalue doesn't make guarantees about iterators; the only general requirement is that the new container has the "same value" as the container it was constructed from had originally.
(You can imagine debug iterator implementations that store a reference to the container, and that reference would become dangling after a move.)
I can move elements within the std::list without invalidating iterators nor references, but how?
But how may I arbitrarily swap the positions of two elements while preserving the values of all iterators?
As suggested by @cpplearner, use .splice()
.
It can operate on individual elements or ranges. It can also transfer elements across lists.
Here's a simple example demonstrating how to move a single element.
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 << ' ';
Related Topics
Locking Strategies and Techniques for Preventing Deadlocks in Code
Understanding (Simple) C++ Partial Template Specialization
Is "Long Long" = "Long Long Int" = "Long Int Long" = "Int Long Long"
Is It a Good Practice to Always Use Smart Pointers
How to Test Whether a Number Is a Power of 2
How to Detect Negative Numbers as Parsing Errors When Reading Unsigned Integers
How to Invoke a User-Defined Conversion Function via List-Initialization
Cannot Open Output File, Permission Denied
Vc2013: Function from Bind Not Compiling
Programmatically Check Whether My MAChine Has Internet Access or Not
Converting Integer into Array of Digits
Stl Vector: Moving All Elements of a Vector
How to Use Wndproc as a Class Function
How to Override Compile Command of Visual Studio 2017 Community
C++: Unresolved External Symbol _Sprintf and _Sscanf in Visual Studio 2015