Cast vector(Derived *) to vector(Base *)

Started by
14 comments, last by MaulingMonkey 16 years, 1 month ago
Actually, my problem is a bit more complicated than the subject gives away. I want to cast a vector< shared_ptr<Derived> > to a vector< shared_ptr<Base> >.
struct InternalManager {
  vector< shared_ptr<XyzConnection> > ActiveConnections;
};

class PublicApi {
  typedef vector< shared_ptr<Connection> > ConnectionVector;
  const ConnectionVector &GetActiveConnections() const {
    return InternalManager::ActiveConnections;
  }
};
I know that technically, I could just reinterpret_cast<> the whole thing, but of course, that's a horrific way to solve my problem. Another solution would be to write my own collection interface:
struct ConnectionList {
  public: virtual size_t size() const = 0;
  public: virtual shared_ptr<Connection> Get(size_t index) = 0;
};
struct XyzConnectionList : public ConnectionList {
  public: virtual size_t size() const { return this->connections->size(); }
  // Darn! Cannot use covariant returns due to shared_ptr<> here!
  public: virtual shared_ptr<Connection> Get(size_t index) {
    return this->connections.at(index);
  }
  public: virtual const shared_ptr<XyzConnection> &GetImpl(size_t index) {
    return this->connections.at(index);
  }
  private: vector< shared_ptr<XyzConnection> > connections;
};
But maybe someone here knows a shorter way that doesn't require me to reimplement a full collection interface with iterators and stuff just to avoid copying a possibly large vector? -Markus-
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
Advertisement
You can't do it, because it's not moral. Casting a vector<Derived*> to a vector<Base*> would imply that a vector<Derived*> IS-A vector<Base*>. But that's not true, because there's something vector<Base*> can do that vector<Derived*> can't: Insert Base*s that aren't Derived*s. Therefore, such a cast could lead to illegal operations.

Now, that's sort of a weak excuse. A const vector<Derived*> is indeed a const vector<Base*>, but you can't do that either. The best you can do is make your function take an iterator pair instead of a container, and template it on the iterator type. Not perfect, but neither's C++.
Quote:Original post by Sneftel
Now, that's sort of a weak excuse. A const vector<Derived*> is indeed a const vector<Base*>, but you can't do that either.


Actually it isn't. For a given Derived object the bit pattern for the Derived * and the Base * for the object aren't necessarily equal.
Quote:Original post by SiCrane
Quote:Original post by Sneftel
Now, that's sort of a weak excuse. A const vector<Derived*> is indeed a const vector<Base*>, but you can't do that either.


Actually it isn't. For a given Derived object the bit pattern for the Derived * and the Base * for the object aren't necessarily equal.

Yes, I meant IS-A in the parametric polymorphic sense. That is, every operation which is defined on const vector<Base*> is defined on const vector<Derived*> (taking covariance into account, and extending the polymorphism to the const_iterators).
That's like arguing a float IS-A double.
Not exactly. The situation with float and double is a little more complicated, because there's no type ordering between them. An operation expecting a double cannot take a float, nor vice versa, without the interposition of a conversion operation which comes up with a new value of the appropriate type. (Admittedly, the fact that this conversion is implicit--something anathema to people who use phrases like "parametric polymorphism"--makes it a difficult area to talk about without ambiguity.)
The situation with const vector<Base*> and const vector<Derived*> is the same, because there's no type ordering between them. An operation expecting a const vector<Base*> cannot take a const vector<Derived*>, nor vice versa, without the interposition of a conversion operation which comes up with a new value of the appropriate type.
Quote:Original post by Cygon
Actually, my problem is a bit more complicated than the subject gives away. I want to cast a vector< shared_ptr<Derived> > to a vector< shared_ptr<Base> >.


You can't, and furthermore, you really don't want to. Returning the vector of connections is probably exposing too much anyway. Try offering an iterator interface instead, or just a member function to get the Nth active connection. In the second case, you should be able to convert to a boost::weak_ptr<Connection> fairly easily to hide the information, and in the first it should still be doable (though you'll be entering the somewhat scary world of iterator adaptation). You don't need a full collection interface - just the parts that outside code are interested in. Why should it care that your active connections are stored in a vector, anyway?
Quote:I want to cast a vector< shared_ptr<Derived> > to a vector< shared_ptr<Base> >.
Whilst some people responding may start down the path of figuring out contrived ways of doing such a thing, I'm going to state that you need to take a step back and explain what let you to the situation where you would want to do this.
Chances are that situation can be re-examinined with the conclusion that there is a better way of doing things. Please provide information about the overall goal.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:The situation with const vector<Base*> and const vector<Derived*> is the same, because there's no type ordering between them. An operation expecting a const vector<Base*> cannot take a const vector<Derived*>, nor vice versa, without the interposition of a conversion operation which comes up with a new value of the appropriate type.

Ah, but the nature of the conversion is different. For float/double, the conversion changes the data fundamentally (for a clearer picture, think of float/int). In contrast, the conversion here would be purely representational, twiddling bits to keep the machine happy. Put differently, in a perfect world, there would be two valid ways to type-check this function (though, of course, C++ chooses a particular one), which would nevertheless produce identical results:
template<typename T>Base* getThird(vector<T> const& v){    return v[3];}vector<Derived*> v;getThird(v);

This topic is closed to new replies.

Advertisement