tags:

views:

130

answers:

4

The following code raises a runtime error:

#include <iostream>
#include <iterator>
#include <ext/slist>

class IntList :  public __gnu_cxx::slist<int> {
public:
    IntList() { tail_ = begin(); } // seems that there is a problem here
    void append(const int node) { tail_ = insert_after(tail_, node); }

private:
    iterator tail_;
};

int main() {
    IntList list;

    list.append(1);
    list.append(2);
    list.append(3);

    for (IntList::iterator i = list.begin(); i != list.end(); ++i) {
        std::cout << *i << " ";
    }

    return 0;
}

Seems that the problem is in the constructor IntList(). Is it because it calls the member function begin()?

A: 

.begin() is invalid if there are no elements in your collection.

Chris Kaminski
:: That's wrong.
wilhelmtell
`begin()` is always valid on a container. If there are no elements in the container, then `begin() == end()` (assuming this GNU extension slist is an implementation of the STL slist, of course).
James McNellis
Learn something new everyday, I guess. :-)
Chris Kaminski
+2  A: 

Looks like You are inserting after end();

In the body of the constructor

IntList() { tail_ = begin(); }

the base class is constructed and it's members can be called, but for an empty list, it should return end();

Maciej Hehl
This looks right; the iterator passed to `insert_after()` must be dereferenceable, and `end()` is not.
James McNellis
So, I want to add the private member tail_ to track the tail of the list so that I can append elements faster. How can I do this?
powerboy
You shouldn't derive from `slist`, to begin with. But to solve that particular problem try calling `insert()` instead.
wilhelmtell
Thx WilhelmTell! Maybe I should use slist as the underlying container of my list, the same way as how <stack> is based on <deque>
powerboy
@powerboy yes, that makes more sense. either bring an `slist` object into `IntList` as a data-member, or derive privately from `slist`. The problem is there only when clients try to create a polymorphic object of `IntList`, like so: `slist* l = new IntList();` and sometime later `delete l;`. When that happens the `slist` part of `IntList` will clean up properly, but the `IntList` won't: it will result in undefined behaviour. If you don't derive publicly from `slist` then users simply can't make a polymorophic object of the concrete `IntList` type, so it's safe.
wilhelmtell
@powerboy if you derive privately from `slist` then remember that inside your class you still have access to `slist`. that means that any member (or friend) of your class can still create and delete a polymorphic type that interfaces on `slist`. In other words, _you_ can still shoot yourself in the foot. For this reason it's a bad idea to derive in any way from a class that has no virtual destructor. But yes, you'd still limit the problem to you shooting yourself in the foot, rather than anyone at all.
wilhelmtell
+2  A: 

Your problem is not in the constructor but in the first call to append(). Because your list is empty begin() equals end(). end() is a valid iterator, but the one after that isn't. To solve that particular problem try calling insert() instead.

That said, a fast peek at <ext/slist> confirms that the destructor of slist isn't virtual, which means slist wasn't intended to be derived from.

wilhelmtell
+1  A: 

The documentation for slist<> indicates that the iterator provided to insert_after must be dereferenceable.

Since your list is empty the begin() returns end() and is thus not dereferenceable.

See documentation here:

http://www.sgi.com/tech/stl/Slist.html

iterator insert_after(iterator pos, const value_type& x)

pos must be a dereferenceable iterator in *this. (That is, pos may not be end().) Inserts a copy of x immediately following pos. The return value is an iterator that points to the new element. Complexity: constant time.

Martin York