Upcasting of templatized types.

This is a discussion on Upcasting of templatized types. within the C++ Programming forums, part of the General Programming Boards category; Suppose you have classes Apple, Orange and Pear that all inherit from a base class Fruit. And suppose your software ...

  1. #1
    Registered User
    Join Date
    May 2009
    Posts
    3

    Upcasting of templatized types.

    Suppose you have classes Apple, Orange and Pear that all inherit from a base class Fruit. And suppose your software has modules that work with vector<Apple>, vector<Orange> and vector<Pear>. Now I'd like to write a generic class FruitStore having a public method addToShelves which takes a vector of fruit and puts it on shelves for sale:

    Code:
    class FruitStore
    {
    private:
        vector<vector<Fruit>> shelves;
    
    public:
        void FruitStore::addToShelves(vector<Fruit>& fruitList)
        {
            shelves.push_back(fruitList);
        }
    }
    But I'm pretty darn sure you can't pass a vector<Apple> to a function expecting a vector<Fruit> (even though such a thing makes sense intuitively). And similarly you can't add a vector<Apple> to a vector<vector<Fruit>>. This is because vector<Apple> does not inherit from vector<Fruit>.

    It's been quite sometime since I've written any C++ (and hopefully you can see past any gross syntactic errors I've made above), but is there any technique one can use to store all these fruit vectors in a single shelf container without having to actually copy, say, each Apple in a vector<Apple> into a new vector<Fruit> (which really seems like a silly thing to have to do)?

    Thanks much,
    Matt Busche

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    For derived types to work correctly in this sort of instance, you would most likely want to store a pointer or reference to the object. Unless have EXACTLY the same layout in memory and the same member functions (in which case, one would start to wonder what the purpose of making several classes actually is).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    May 2009
    Posts
    3
    Mats,

    Uhmm...yeah...I definitely don't want to store the lists by value. Should be more like:

    Code:
    class FruitStore
    {
    private:
        vector<vector<Fruit&>&> shelves;
    
    public:
        void FruitStore::addToShelves(vector<Fruit&>& fruitList)
        {
            shelves.push_back(fruitList);
        }
    }
    It's been many years since I've touched C++. Hopefully that's closer.

    But that doesn't help anything at all does it? Again, a vector<Apple&> does not inherit from vector<Fruit&>, so I can't store a vector<Apple&> in something that holds a vector<Fruit&>, right?

    Thanks,
    Matt Busche

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Technically you could do this with something like boost::variant. Adding to shelves would be easy, but using the stored vectors would be a bit harder. (In this example, inheritance actually doesn't matter at all.)

    Code:
    #include <vector>
    #include <boost/variant.hpp>
    
    class Fruit {};
    
    class Apple: public Fruit {};
    
    class Orange: public Fruit {};
    
    typedef boost::variant<std::vector<Apple>, std::vector<Orange> > FruitVectors;
    
    class FruitStore
    {
    private:
        std::vector<FruitVectors> shelves;
    
    public:
        template <class FruitVector>
        void FruitStore::addToShelves(const FruitVector& fruitList)
        {
            shelves.push_back(fruitList);
        }
    };
    
    int main()
    {
        FruitStore store;
        std::vector<Apple> apples;
        std::vector<Orange> oranges;
        store.addToShelves(apples);
        store.addToShelves(oranges);
    
        //These won't compile: FruitStore only accepts vector<Apple> and vector<Orange>
        //store.addToShelves(Apple());
        //store.addToShelves(std::vector<int>());
    }
    But then, what is the difference between the various fruit types?
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Eh? If Apple is not a descendant of Fruit, you can not cast Apple into Fruit or Fruit into Apple and expect to get anything meaningful out of this.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,165
    Note that vector<vector<T>> syntax is illegal. The standard demands it be: vector<vector<T> >.
    Also note you have to use pointers to Fruit since references must be initialized.
    It might expect it to look like vector< vector<Fruit*> >.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.
    For information on how to enable C++11 on your compiler, look here.
    よく聞くがいい!私は天才だからね! ^_^

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,892
    Given vector<Apple> apples and vector<Orange> oranges, you can do
    Code:
    struct addr { template <typename T> T* operator()(T& t) { return &t; } };
    
    vector<Fruit*> fruits(apples.size() + oranges.size());
    transform(apples.begin(), apples.end(), fruits.begin(), addr());
    transform(oranges.begin(), oranges.end(), fruits.begin() + apples.size(), addr());
    There is no way to store a vector<Apple> in a vector<vector<Fruit>>, no matter how many levels of indirection you add. The two types are completely unrelated as far as substitutability is concerned.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by CornedBee View Post
    There is no way to store a vector<Apple> in a vector<vector<Fruit>>, no matter how many levels of indirection you add. The two types are completely unrelated as far as substitutability is concerned.
    That's what I tried to say, but apparently failed to get through.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #9
    Registered User
    Join Date
    May 2009
    Posts
    3
    Thank you all for your attempts to help me. Again, I apologize for the gross errors in my original code sketch. Although I asked for folks to see past them they seem to be a point of great distraction. I should have dusted off a compiler before trying to post anything here.

    I don't think any of your posts offer me a solution to my problem. Perhaps there is no good solution to my problem -- but I would find that highly surprising. All I can think to do now is to force all modules in my entire application to use only vector<Fruit*> so that these vectors can be manipulated by facilities that work on the broad type Fruit. I guess this wouldn't be the end of the world, but it does seem rather silly to have to force a module that works only with Apples to store their Apples as lists of the base-type Fruit -- forcing them to downcast that Fruit back to an Apple every time they pop an Apple off such a list.

    My responses to individual posts follow:

    Anon,

    Your post is ALMOST helpful in that it does give me a way to store an enumerated list of specific vector types in a single container, but after a brief perusal of Boost::variant, I agree with your comment "using the stored vectors would be a bit harder". They give an example that uses a visitor pattern to access content based on type. That might be applicable to this problem -- I'd have to play with it -- the visitor pattern makes me dizzy. BUT the whole Boost::variant facility seems to demand that you be able to enumerate the specific types you are going to be working with ahead of time: I surely don't want my FruitStore bound to a predetermined list of types. If someone tomorrow comes along and wants to put Bananas in my store, I don't want to have to edit my FruitStore class and add Banana to the list of types acceptable in the store.

    Unless I'm missing something (quite possible), I don't think Boost::variant is going to help me.

    Elysia,

    Nice catch on the syntactic error where I omitted the space between the two closing angle brackets. It's been a while since I've done any C++ (some of the simplest constructs look quite alien to me now). I do remember that references must be initialized, but I don't see why that would disallow one from having a container of references to other objects. I'll have to try this out and try to figure out why it breaks. But in any case, pointers are fine. Let the shelves be a vector of pointers to vectors of pointers to Fruit: vector<vector<Fruit*>* >. But this is all "pointless" since none of this gets me any closer to solving my real problem. You can't store &vector<Apple*> in a vector<vector<Fruit*>* > because vector<Apple* > is not a subclass of vector<Fruit*>. I think I need something completely different. I'm hoping some smart person can tell me what that is.

    CornedBee,

    Forgive me if I'm misinterpreting, but It looks to me like your giving me a slick way to do a one-by-one copy of entries in my apple list and entries in my orange list into a new list of fruit. This works fine, but I don't want to incur the overhead of moving oranges out of my orange bag and apples out of my apple bag into a new fruit bag. I just want to store references (or pointers) to the original bags.

    And as you nicely stated you can't store a vector<Apple*> in a vector<vector<Fruit*>* >. That's why I'm here asking for help. Can you suggest some other construct that achieves my goals of allowing some module that wishes to work with containers of Apples to pass that container to some other more general utility that operates on containers of Fruit without having to do some Order N transmutation of the container of Apples into a new Container of Fruit?

    Matsp,

    I think your missing an important point. Apple IS a descendant of Fruit. But vector<Apple*> is NOT a descendant of vector<Fruit*>. The fact that it is not, is what's giving me grief. Intuitively, if you have a utility that manipulates a vector<Fruit*>, you'd expect that utility to be able to manipulate a vector of a more specific type: vector<Apple*>, but the STL does not appear to allow for that. As CornedBee clearly stated, although there is an inheritance relationship between the contents of these vectors, there is no inheritance relationship between the vectors themselves.

  10. #10
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,158
    Quote Originally Posted by Elysia View Post
    Note that vector<vector<T>> syntax is illegal. The standard demands it be: vector<vector<T> >.
    The inability of the lexical analysis phase of the compiler to properly deal with '>>' is stupid, always has been, and will be rectified in C++0x as far as I know.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,165
    I can't think of a really good object-oriented way of doing it without some "ugly" parts.
    But the "real" solution should be, if you ask me, to manipulate your objects through Fruit. The best solution.
    However, there is another way that I can think of, a safe way with extra indirections. Consider:

    Code:
    enum Errors
    {
        ERR_NOT_AN_APPLE,
        ERR_NOT_AN_ORANGE
    };
    
    class VectorFruit
    {
    public:
        virtual Apple& GetApple() = 0;
        virtual Orange& GetOrange() = 0;
        virtual bool IsApple() = 0;
        virtual bool IsOrange() = 0;
    };
    
    class VectorApple
    {
    public:
        VectorApple(...): m_Fruit(...) { }
        virtual Apple& GetApple() { return m_Fruit; }
        virtual Orange& GetOrange() { throw ERR_NOT_AN_ORANGE; }
        virtual bool IsApple() { return true; }
        virtual bool IsOrange() { return false; }
    
    protected:
        Apple m_Fruit;
    };
    
    class VectorOrange
    {
    public:
        VectorOrange(): m_Fruit(...) { }
        virtual Apple& GetApple() { throw ERR_NOT_AN_APPLE; }
        virtual Orange& GetOrange() { return m_Fruit; }
        virtual bool IsApple() { return false; }
        virtual bool IsOrange() { return true; }
    
    protected:
        Orange m_Fruit;
    };
    You can code the constructors to take whatever parameters you want to pass to the Apple/Orange constructor.
    This isn't much different from other approach mentioned, however. It isn't very nice or clean.
    But it's safe, at least.
    Now you can store std::vector< std::vector<VectorFruit*> > and use the appropriate interface to get an apple or orange and it will throw if it's not such a type stored.

    Quote Originally Posted by brewbuck View Post
    The inability of the lexical analysis phase of the compiler to properly deal with '>>' is stupid, always has been, and will be rectified in C++0x as far as I know.
    I do agree, but the point is that the standard also forbids it (C++0x), so we must endure it.
    It will indeed be fixed in C++0x to everyone's pleasure.
    Last edited by Elysia; 05-26-2009 at 01:24 PM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.
    For information on how to enable C++11 on your compiler, look here.
    よく聞くがいい!私は天才だからね! ^_^

  12. #12
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,892
    Forgive me if I'm misinterpreting, but It looks to me like your giving me a slick way to do a one-by-one copy of entries in my apple list and entries in my orange list into a new list of fruit.
    Not quite. It's a slick way of creating a list of references to all the fruit you have in those other containers.

    It is in fact possible to create a list that simply references all the containers you have and traverses them in order. However, such a solution is not trivial, and involves some amount of custom code on your part. The key element is type erasure. Basically you create a base type FruitContainerBase, which has a set of virtual functions suitable for traversing a container of fruit. Then you create a templated class FruitContainerImpl<F>, where F is a concrete Fruit type. FCI<F> holds a pointer to a vector<F> and implements its virtual functions by traversing this vector. Finally you create a FruitContainer which holds a list of FruitContainerBase pointers, into which a templated add function can insert suitable FruitContainerImpl instances. FruitContainer offers traversal over all the FruitContainerImpl instances it holds.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. invalid types 'int[int]' for array subscript
    By kolistivra in forum C++ Programming
    Replies: 6
    Last Post: 12-11-2010, 11:57 AM
  2. Replies: 6
    Last Post: 08-23-2008, 01:16 PM
  3. How to convert integral types into pointer types?
    By rohit99 in forum C++ Programming
    Replies: 3
    Last Post: 03-20-2008, 09:57 AM
  4. The Interactive Animation - my first released C program
    By ulillillia in forum A Brief History of Cprogramming.com
    Replies: 48
    Last Post: 05-10-2007, 02:25 AM
  5. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21