tags:

views:

144

answers:

5

I have a simple struct

struct A {
  int t;
  int s;
};

Those were contained in std::list<A>. The problem is that I group A depending on s. I can avoid duplication of s if I'd wrap A in another struct and keep s in a separated container. This has a lot of benefits (e.g. I don't need to do stupid and potentially dangerous things like AContainer[0].s to get the value of s for the whole list). The wrapper would look like this:

struct AList {
   std::list<A> list;
   int s;
}

The problem is that I'd still like my AList to behave as if it were a std::list<A>. This is simple if I replicate the whole interface of std::list but has a few drawbacks: changing the container would involve inspecting all functions that expose the interface, and the huge amount of duplicated code. On the other hand: just using the list makes the code a lot less readable.

Are there any standard solutions or helper classes for such a case? Is inheriting from std::list a good idea? I have never seen it before and most people argue that STL containers don't have virtual dtors. This wouldn't be a problem for me in this situation but is probably relevant to the larger question here.

+4  A: 

Why are you using a list to start with? It sounds like what you probably want is something like std::map<int, std::vector<int> >.

It's hard to answer your question about inheriting from std::list without knowing a lot more about why you're choosing that to start with. Though it's not absolutely certain, it sounds to me like the map I mentioned above, possibly combined with a special-purpose iterator would probably do what you want a lot more cleanly.

Edit: with a floating point type as the "key", things are a bit more difficult. A problem will arise if there's ever any possibility that your key might end up holding a NaN value though. A map/multimap requires a strict weak ordering, which NaN's don't provide (e.g. a NaN won't compare as being equal to anything, not even itself).

As far as dealing with "nearly equal" values go, I'd just ignore it. All it'll cause is that it might unnecessarily store a few more "key" values than necessary, but as you iterate through your "list", you'll still just get them in order, and it's up to client code to decide which ones it considers similar enough to qualify as equal. A map or multimap can take a comparator, but for this purpose you do not want to use one that does approximate comparisons. It can/will cause a problem because it can decide that a==b, and b==c, but a!=c (nearly unavoidable for any approximate comparison).

Jerry Coffin
The list is mainly for interfacing with code. The int makes things look much easier. It's actually a double and comparing doubles with < can be tricky. Even comparing them with some custom comparator that takes relative error into account can be tricky. Also, it is possible that I would need more than just one value to decorate the list.
pmr
+2  A: 

Can't you just use the AList::list member whenever you want to access the actual list? There is no need to replicate the list's interface if you can just use the actual list when you need it:

AList ls = ...;
somefunc(ls.list.begin(), ls.list.end()); // accessing the list

Edit: If you want some syntactic sugar to make it look nicer you could add an operator() that gives access to the contained list:

struct AList {
   std::list<A> list;
   ...
   std::list<A>& operator()() {
      return list;
   }
};

This way you can access the list by "calling" the variable, which is quite concise syntax:

AList ls = ...;
somefunc(ls().begin(), ls().end()); // accessing the list

And on an unrelated note: Usually in most cases std::vector is more flexible and has better performance than std::list.

sth
I wanted to talk about that in the actual question but forgot. This makes code look very inelegant to me. Maybe I'm just picky and should just go with it.
pmr
+1  A: 

Why not use a STL multimap instead?

from http://www.cplusplus.com/reference/stl/multimap/

Multiple-key map Maps are a kind of associative containers that stores elements formed by the combination of a key value and a mapped value, much like map containers, but allowing different elements to have the same key value.

Also, as far as I know it's usually not advised to inherit from STL containers directly. They don't support polymorphism well (if I recall correctly), methods not being virtual for efficiency and such. Some more info: http://en.allexperts.com/q/C-1040/possible-inherit-classes-STL.htm

catchmeifyoutry
A: 

If I understand right, you have a bunch of A values and want all them grouped with the S value. One S for many As.

In that case, yes you can just inherit from the list. It might be a little slower depending on your compiler but it will work just like a list of As except you have an S value as well.

Charles Eli Cheese
A: 

You probably don't need to expose all the functions of the container. Have a look at how boost::range works. Just expose the iterators, and use stl algorithms to do the rest. IIRC most stl algorithm implementations are specialised for particular iterator types so that things like size() are constant-time for random-access iterators.

If you are worried about what happens when you change the container type, then it sounds like you aren't making use of any list-specific functionality. In which case, maybe all you need to expose is begin(), end() and an insert iterator.

Robert Tuck