STL containers and constness

Started by
8 comments, last by Cornstalks 10 years, 6 months ago

Hi

I'm facing a problem which I'm unable to solve using STL containers.

Consider this:


class Mesh
{
public:
   const std::list<SubMesh>& getSubmeshList();
}

I want to be able to modify the objects contained in the list returned by Mesh::getSubmeshList, but want to disable access to inserting/erasing items from the collection. However, since I'm returning a const reference to the list, I can only use a const_iterator which only gives me access to const items.

Is there any way around this without using const_cast? Should I rethink my design?

Advertisement

One option is to return a new container that contains pointers to the SubMesh objects that is store in your Mesh object's list. Another is to make your list contain SubMesh pointers in the first case, which would make the const reference contain const pointers to non-const SubMeshes.

One alternative is to do something like:


class Mesh
{
public:
    std::list<SubMesh>::iterator getSubmeshListBegin()
    {
        return submeshList.begin();
    }

    std::list<SubMesh>::iterator getSubmeshListEnd()
    {
        return submeshList.end();
    }
};
And then just operate on the list using the range iterators. This isn't 100% the same, and would require an additional method if you want an easy way to get the size of the list too, but I can't think of anything closer. Also, if you're using C++11, you can name the methods begin() and end() and then use range-based for loops if you wanted.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

Thanks! Great ideas!

I had thought of exposing the iterators before, but it looks like it may polute the method list in case I need to expose many lists.

However I just thought of creating a template wrapper class that could expose this info, like so:


template <class T>
class ReadOnlyContainer
{
public:
    ReadOnlyContainer( T& originalContainer );

    T::iterator begin();
    T::iterator end();
    size_t size();

private:
   T& _container;
}

class Mesh
{
public:
     ReadOnlyContainer< std::list<SubMesh> > getSubmeshes();
}

Do you think it's a nice idea? Am I overlooking anything?

Do you think it's a nice idea? Am I overlooking anything?

The only thing that really worries me is this: "in case I need to expose many lists." Perhaps too much is being exposed (or too much is being contained in a single class) if you get to this point.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]


The only thing that really worries me is this: "in case I need to expose many lists." Perhaps too much is being exposed (or too much is being contained in a single class) if you get to this point.

You're right. I don't intend to expose much on this specific Mesh class. I just to avoid an eventual future corner-case where I need to expose several lists. I prefer to think of a better/prettier solution if there is any in order to avoid refactoring later.

Thank you for the heads up though :)

I would just advise you against too much forward-thinking. Code for 'now', because you can't possibly predict the future.

When (or rather, if) you need feature X, you will also need Y. And you will have a concrete use case to reason against. Then you will come up with much better interface.

If you still persist, I'd rather make the Mesh class itself a container, like Cornstalks suggested.

Why do you need so much to allow modify but not allow insertions & deletions?

Code external to the Mesh class should not depend on its implementation details: in this case, the fact that submeshes are stored in a std::list.

I would suggest the following Mesh interface:


class Mesh
{
public:
  size_t GetSubMeshCount() const;
  SubMesh& GetSubMesh(size_t index);
  const SubMesh& GetSubMesh(size_t index) const;
};

Furthermore, std::list has a memory and performance overhead not justified in this case. Use an std::vector or a raw array or even a buffer of fixed size stored in the Mesh class itself (and a dynamically-allocated array as a fallback if the submeshes count exceeds the fixed limit). Please note that here I am assuming that this Mesh class is for the runtime engine, where performance is critical, and not a tool. In the latter case use whatever container makes your life easier.

Furthermore, std::list has a memory and performance overhead not justified in this case.

We don't know that for sure. The major reason one would (should) use a std::list is iterator validity after pushing back and erasing. If the OP does not need iterator validity after these types of operations, then sure, something like std::vector that has better cache performance and O(1) random access would be ideal. But that depends on details the OP did not provide.

[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

This topic is closed to new replies.

Advertisement