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

Started by
14 comments, last by MaulingMonkey 16 years, 1 month ago
Quote:Original post by Sneftel
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.

For that argument to hold you have to assume that Derived * and Base * have the same bit pattern representation, which is not a valid assumption. In order for a const vector<Derived *> to be passed to a function that accepts a const vector<Base *>, you need to go down the line and convert each and every stored Derived * to a Base * before the body of the function is invoked. This can't be done inline since the objects are const, so in other words creating a temporary just like a float to double conversion that happens when you try to invoke a float function with a double or vice versa.
Advertisement
I'm not saying that it's pragmatically possible in C++. I'm just saying that other than the underlying data representation, there's no good excuse not to be able to extend the type system with that conversion, given that the outward interface of the two types are compatible.
You are able to extend C++'s type system with that relationship, with an explicit creation of a temporary. Or if you feel like it:
template <typename T, typename U>const vector<T> const_vector_cast(const vector<U> & value) {  return vector<T>(value.begin(), value.end());}

But again, since this involves the creation of a temporary, it doesn't seem any more of an IS-A relationship than float-double.
Quote:Original post by iMalc
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.


I just wanted to know whether there's a safe and elegant way to achieve such a cast that I'm missing, not to perform it at any cost. If there isn't, I can simply write a proxy for the vector, create my own collection interface, return a copied vector<Base *> or redesign my interface of course.

I'm not some bloody amateur who'd go ahead and rape C++ if my design doesn't work out :)

-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.
Quote:Original post by Cygon
Quote:Original post by iMalc
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.


I just wanted to know whether there's a safe and elegant way to achieve such a cast that I'm missing, not to perform it at any cost. If there isn't, I can simply write a proxy for the vector, create my own collection interface, return a copied vector<Base *> or redesign my interface of course.


/me points back excitedly at his previous post. Just give back iterators (you'll probably need/want to do some adaptation so that you create an iterator over the pointed-at Connection objects), or individual elements on demand.

... Or then again, you could check again if you really need the flexibility that a vector of shared_ptr gives you - i.e. the flexibility to share objects between containers. If you only want to store objects while respecting polymorphism, and the container "owns" its elements, then a boost::ptr_vector of Connection would be more appropriate. (Although I would still change the interface, even though this would let you return the vector and work with it polymorphically.)
Quote:Original post by Sneftel
I'm not saying that it's pragmatically possible in C++. I'm just saying that other than the underlying data representation, there's no good excuse not to be able to extend the type system with that conversion, given that the outward interface of the two types are compatible.


I don't know, you're basically introducing the concept of const semi-inheritance to an already complex system just to be able to convert between two classes which are effectively leaf nodes of a inheritance tree. Aside from type, they also specify concretely the allocator to be used.

A much simpler solution would seem to be to go up the inheritance tree.

While we're unable to inject parent classes into the C++ standard library hierarchy, we are entirely able to do the next best thing and conjure up a secondary reference/view class which can handle this (admittedly nonexistant) parent, and we have ample opportunity to handle upcasting, differing allocation methods, etc. there.

Call it const_vector_ref, to take a page from Boost.MultiArray's naming conventions. Of course, this gets us back to the apparent needless complexity point, and one wonders if the solution couldn't be to just template the function based on the container or iterators.

If that wasn't an option, my next instinct would be to just put together a pair of industry::virtual_random_access_iterators to industry::static_cast_iterators, to the original container's iterators, since I've already written this code.

Alternatives to static_cast_iterator would include some as of yet unwritten implicit_cast_iterator, or hacking implicit casting into my virtual iterators.

Since we're already presupposing const access (to avoid the original moral objection raised of DerivedFromBase2* in a DerivedFromBase1* container), that should provide all the necessary access anyways (unless you really really need .capacity(), end-begin for the equivilant of .size() not being enough for your greedy arse). Of course, the virtual access for every single operation may make copying actually superior in performance, but...

(in case you're wondering where the iterator source is)

EDIT:

To get back closer to where the OP is probably thinking about, wanting to get connections by index rubs me just about every wrong way possible. Connections come and go, so I'm left wondering why you'd use this presumably unstable key in the first place. Why you have separate collections of XyzConnection that need to be handled elsewhere as collections of Connection also leaves me wondering. Combined with the example identifiers which hopefully don't even exist (XyzConnection), the whole shebang feels horribly over engineered, over thought in the wrong directions, and Java-like. Enterprisey. Hopefully that's just the opaqueness, but it's hard to point out the best direction to go when the original premise feels faulty in the first place. Not enough data.

[Edited by - MaulingMonkey on March 7, 2008 3:52:48 PM]

This topic is closed to new replies.

Advertisement