std::list, move item in list using iterators only - c++

It seems to me given what I know about linked lists that this should be possible but I haven't found anywhere that has the answer so I'm asking here.
Given two iterators to items in the same list. I'd like to take the item pointed to by iterator "frm" and "insert" it into the list before the item pointed to by iterator "to".
It seems that all that is needed is to change the pointers on the items in the list pointing to "frm" (to remove "frm"), then changing the pointers on the item pointing at "to" so that it references "frm" then changing the pointers on "frm" node to point to "to".
I looked everywhere for this and couldn't find an answer.
NOTE that I cannot use splice as I do not have access to the list only the iterators to the items in the list.
template <typename T>
void move(typename std::list<T>::iterator frm, typename std::list<T>::iterator to) {
//remove the item from the list at frm
//insert the item at frm before the item at to
}

Iterators contain the minimal information required to point to a piece of data, what you are missing is the fact that linked lists have other bookkeeping that go along with it as well, so essentially the list class looks something like the following
template <typename Type>
class list {
int size; // for O(1) size()
Type* head;
Type* tail;
class Iterator {
Type* element;
// no back pointer to list<Type>*
};
...
};
And to remove an element from the list you would need to update those data members as well. And to do that an iterator must contain a back pointer to the list itself, which is not required as per the interface offered for most iterators. Notice also that the algorithms in the STL do not actually modify the bookkeeping for the containers the operate on, only maybe swap elements, and rearrange things.
I would encourage you took look into the <algorithm> header, as well as into facilities like std::back_inserter and std::move_iterator to get an idea of how iterators are wrapped to actually modify the container they represent.

The implementation of this is implementation defined but the c++ standard allows the use of iter_swap though it doesn't do this exactly. This maybe optimized to swap the pointers on the values held in the linked list similar to what I have described effectively reordering the items in the list without a full swap needed.
iter_swap() versus swap() -- what's the difference?

Related

Boost.Intrusive - Constant time Iterator

Boost.Intrusive can get an Iterator out of an Object-Ref or Object-Pointer in constant time (see here: https://www.boost.org/doc/libs/1_72_0/doc/html/intrusive/usage_when.html). How does that work? Why is this not possible for standard containers?
Intrusive containers by definition have the information contained inside of elements to know how they're located in the container. A simple example is an intrusive linked list:
struct Object {
Object* next;
int some_data;
};
Obviously, if I have a reference or pointer to Object, I can easily find the next field, and from there, move to the next element, this is just accessing a member, which is O(1), thus constant time iterators.
With non-intrusive containers, it would look like this:
struct Object {
int some_data;
};
Suppose I have a std::vector of these, and a pointer or reference to Object, I can't work backwards from that to where it is in the std::vector without scanning the container to find it (a O(n) operation).

Direct access to element of STL list with a pointer

Is it possible to access an element of an STL linked list directly by it's pointer? My program requires quick insertion, removal and access of elements.
STL containers use iterators instead of pointers. If you have an iterator that points to an element of your linked list, you can access element's data through it, insert at the iterator's position using the list's insert method, and delete at iterator's position using the erase method.
Instead of using STL linked list, you may want to define your own linked list implementation using pointers. For example:
template <class E>
struct Node {
E data;
Node * next;
};
So define a Node class that will be an element in the linked list. As Kerrek SB suggested, redesigning the program with iterators in mind might be quicker and better in the long term.

Can I delete a item from std::list based on pointer value?

This code is written on fly, plz ignore syntax mistakes if any.
std::list<MY_STRUCT> myList;
MY_STRUCT theStruct;
myList.push_back( theStruct );
myList.push_back( theStruct );
// assume I store the pointer of the last item (the 2nd item in this case).
MY_STRUCT * item2 = &myList.back();
// I added another item
myList.push_back( theStruct );
// now I want to delete item2 that I stored bases on its pointer.
// Can myList.remove_if(...) help if so how?
I want to delete the middle item in the list by its pointer (assume I have the pointer value).
I know I can iterate through the list and look for this pointer but is there a better way? Does STL provide a function to do it..Can I use remove_if() in this case to delete the item?
Instead of keeping a pointer to the object you want to remove, why not keep an iterator?
std::list<MY_STRUCT>::iterator item2 = --mylist.end();
The remove_if algorithm doesn't actually remove anything, it just shifts stuff around. It has no knowledge of the container that the iterators point to. Of course the member function remove_if of std::list is a different thing altogether as pointed out in the comments.
Sure, list::remove_if uses whatever condition you give it. For example
template <typename T>
struct AddressIs {
T *ptr;
AddressIs(T *ptr) : ptr(ptr) {}
bool operator()(const T &object) const {
return ptr == &object;
}
};
myList.remove_if(AddressIs<MY_STRUCT>(item2));
Mankarse's point is good though - if you can use an iterator instead of a pointer to identify the item you're interested in, then you don't need to mess about with this.
Beware also that we're relying here on the fact that the address of an item in a list stays the same forever. That isn't always true of all collections, for example vector might have to relocate all the data when you call push_back. If it does, then your middle item is no longer pointed to by item2. Each collection documents which operations can invalidate iterators and/or references to elements.
Instead of getting the back item, you could get the end iterator, make sure it's not begin, decrement by one to point to the last item, and then erase that iterator directly whenever you want.
I think remove_if is a little bit of overkill for what zadane is trying to do. All that needs to be accomplished is to save the location or value of an item in order to delete that specific item later.
As Mark suggested you can store the iterator to the object and use it to delete the item with an erase call, like below:
MY_STRUCT struct;
myList.push_back(struct);
myList.push_back(struct);
std::list<MY_STRUCT>::iterator del_it = myList.end() - 1;
myList.erase(del_it);
Or, if your structure has the == operator defined for MY_STRUCT, you can store the value of the object itself and use the remove method
MY_STRUCT struct1;
MY_STRUCT struct2;
myList.push_back(struct1);
myList.push_back(struct2);
myList.remove(struct2);
Of course if you make your list a list of pointers then you don't have to worry about the == operator as it is already defined for pointer types. Just make sure that if you're iterating through the list and call erase, you need to update your iterator with the returned value.
Also, the remove method removes all elements of the passed value, so if you only want to remove 1 item at a time save the iterator and not the value.
This code is untested so I welcome any corrections.

Hybrid vector/list container?

I'm in need of a container that has the properties of both a vector and a list. I need fast random access to elements within the container, but I also need to be able to remove elements in the middle of the container without moving the other elements. I also need to be able to iterate over all elements in the container, and see at a glance (without iteration) how many elements are in the container.
After some thought, I've figured out how I could create such a container, using a vector as the base container, and wrapping the actual stored data within a struct that also contained fields to record whether the element was valid, and pointers to the next/previous valid element in the vector. Combined with some overloading and such, it sounds like it should be fairly transparent and fulfill my requirements.
But before I actually work on creating yet another container, I'm curious if anyone knows of an existing library that implements this very thing? I'd rather use something that works than spend time debugging a custom implementation. I've looked through the Boost library (which I'm already using), but haven't found this in there.
If the order does not matter, I would just use a hash table mapping integers to pointers. std::tr1::unordered_map<int, T *> (or std::unordered_map<int, unique_ptr<T>> if C++0x is OK).
The hash table's elements can move around which is why you need to use a pointer, but it will support very fast insertion / lookup / deletion. Iteration is fast too, but the elements will come out in an indeterminate order.
Alternatively, I think you can implement your own idea as a very simple combination of a std::vector and a std::list. Just maintain both a list<T> my_list and a vector<list<T>::iterator> my_vector. To add an object, push it onto the back of my_list and then push its iterator onto my_vector. (Set an iterator to my_list.end() and decrement it to get the iterator for the last element.) To lookup, look up in the vector and just dereference the iterator. To delete, remove from the list (which you can do by iterator) and set the location in the vector to my_list.end().
std::list guarantees the elements within will not move when you delete them.
[update]
I am feeling motivated. First pass at an implementation:
#include <vector>
#include <list>
template <typename T>
class NairouList {
public:
typedef std::list<T> list_t;
typedef typename list_t::iterator iterator;
typedef std::vector<iterator> vector_t;
NairouList() : my_size(0)
{ }
void push_back(const T &elt) {
my_list.push_back(elt);
iterator i = my_list.end();
--i;
my_vector.push_back(i);
++my_size;
}
T &operator[](typename vector_t::size_type n) {
if (my_vector[n] == my_list.end())
throw "Dave's not here, man";
return *(my_vector[n]);
}
void remove(typename vector_t::size_type n) {
my_list.erase(my_vector[n]);
my_vector[n] = my_list.end();
--my_size;
}
size_t size() const {
return my_size;
}
iterator begin() {
return my_list.begin();
}
iterator end() {
return my_list.end();
}
private:
list_t my_list;
vector_t my_vector;
size_t my_size;
};
It is missing some Quality of Implementation touches... Like, you probably want more error checking (what if I delete the same element twice?) and maybe some const versions of operator[], begin(), end(). But it's a start.
That said, for "a few thousand" elements a map will likely serve at least as well. A good rule of thumb is "Never optimize anything until your profiler tells you to".
Looks like you might be wanting a std::deque. Removing an element is not as efficient as a std::list, but because deque's are typically created by using non-contiguous memory "blocks" that are managed via an additional pointer array/vector internal to the container (each "block" would be an array of N elements), removal of an element inside of a deque does not cause the same re-shuffling operation that you would see with a vector.
Edit: On second though, and after reviewing some of the comments, while I think a std::deque could work, I think a std::map or std::unordered_map will actually be better for you since it will allow the array-syntax indexing you want, yet give you fast removal of elements as well.

Item in multiple lists

So I have some legacy code which I would love to use more modern techniques. But I fear that given the way that things are designed, it is a non-option. The core issue is that often a node is in more than one list at a time. Something like this:
struct T {
T *next_1;
T *prev_1;
T *next_2;
T *prev_2;
int value;
};
this allows the core have a single object of type T be allocated and inserted into 2 doubly linked lists, nice and efficient.
Obviously I could just have 2 std::list<T*>'s and just insert the object into both...but there is one thing which would be way less efficient...removal.
Often the code needs to "destroy" an object of type T and this includes removing the element from all lists. This is nice because given a T* the code can remove that object from all lists it exists in. With something like a std::list I would need to search for the object to get an iterator, then remove that (I can't just pass around an iterator because it is in several lists).
Is there a nice c++-ish solution to this, or is the manually rolled way the best way? I have a feeling the manually rolled way is the answer, but I figured I'd ask.
As another possible solution, look at Boost Intrusive, which has an alternate list class a lot of properties that may make it useful for your problem.
In this case, I think it'd look something like this:
using namespace boost::intrusive;
struct tag1; struct tag2;
typedef list_base_hook< tag<tag1> > base1;
typedef list_base_hook< tag<tag2> > base2;
class T: public base1, public base2
{
int value;
}
list<T, base_hook<base1> > list1;
list<T, base_hook<base2> > list2;
// constant time to get iterator of a T item:
where_in_list1 = list1.iterator_to(item);
where_in_list2 = list2.iterator_to(item);
// once you have iterators, you can remove in contant time, etc, etc.
Instead of managing your own next/previous pointers, you could indeed use an std::list. To solve the performance of remove problem, you could store an iterator to the object itself (one member for each std::list the element can be stored in).
You can extend this to store a vector or array of iterators in the class (in case you don't know the number of lists the element is stored in).
I think the proper answer depends on how performance-critical this application is. Is it in an inner loop that could potentially cost the program a user-perceivable runtime difference?
There is a way to create this sort of functionality by creating your own classes derived from some of the STL containers, but it might not even be worth it to you. At the risk of sounding tiresome, I think this might be an example of premature optimization.
The question to answer is why this C struct exists in the first place. You can't re-implement the functionality in C++ until you know what that functionality is. Some questions to help you answer that are,
Why lists? Does the data need to be in sequence, i.e., in order? Does the order mean something? Does the application require ordered traversal?
Why two containers? Does membership in the container indicated some kind of property of the element?
Why a double-linked list specifically? Is O(1) insertion and deletion important? Is reverse-iteration important?
The answer to some or all of these may be, "no real reason, that's just how they implemented it". If so, you can replace that intrusive C-pointer mess with a non-intrusive C++ container solution, possibly containing shared_ptrs rather than ptrs.
What I'm getting at is, you may not need to re-implement anything. You may be able to discard the entire business, and store the values in proper C++ containers.
How's this?
struct T {
std::list<T*>::iterator entry1, entry2;
int value;
};
std::list<T*> list1, list2;
// init a T* item:
item = new T;
item->entry1 = list1.end();
item->entry2 = list2.end();
// add a T* item to list 1:
item->entry1 = list1.insert(<where>, item);
// remove a T* item from list1
if (item->entry1 != list1.end()) {
list1.remove(item->entry1); // this is O(1)
item->entry1 = list1.end();
}
// code for list2 management is similar
You could make T a class and use constructors and member functions to do most of this for you. If you have variable numbers of lists, you can use a list of iterators std::vector<std::list<T>::iterator> to track the item's position in each list.
Note that if you use push_back or push_front to add to the list, you need to do item->entry1 = list1.end(); item->entry1--; or item->entry1 = list1.begin(); respectively to get the iterator pointed in the right place.
It sounds like you're talking about something that could be addressed by applying graph theory. As such the Boost Graph Library might offer some solutions.
list::remove is what you're after. It'll remove any and all objects in the list with the same value as what you passed into it.
So:
list<T> listOne, listTwo;
// Things get added to the lists.
T thingToRemove;
listOne.remove(thingToRemove);
listTwo.remove(thingToRemove);
I'd also suggest converting your list node into a class; that way C++ will take care of memory for you.
class MyThing {
public:
int value;
// Any other values associated with T
};
list<MyClass> listOne, listTwo; // can add and remove MyClass objects w/o worrying about destroying anything.
You might even encapsulate the two lists into their own class, with add/remove methods for them. Then you only have to call one method when you want to remove an object.
class TwoLists {
private:
list<MyClass> listOne, listTwo;
// ...
public:
void remove(const MyClass& thing) {
listOne.remove(thing);
listTwo.remove(thing);
}
};