Prev: restriction on inheritance
Next: information for find
From: Robi-Wan-Kenobi on 19 May 2010 01:32 Hi all, the following code isn't compiling: template<class T> class containerBase { }; class aBase { }; class aDerived : public aBase { }; void aFunc(containerBase<aBase*>& aContainer) { } void aTestFunc() { containerBase<aDerived*> lContainer; aFunc(lContainer); } The compiler (VC 2005) complains containerBase<aDerived*> can't be converted to containerBase<aBase*>. But i can't see a reason why. I could do some brute force casting but i'd prefer some more elegant way. Is there some common pattern for it? thanx, Robert -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Chris Uzdavinis on 19 May 2010 07:26 On May 19, 11:32 am, Robi-Wan-Kenobi <robert.ka...(a)gmx.de> wrote: > Hi all, > > the following code isn't compiling: > > template<class T> class containerBase { }; > class aBase {}; > class aDerived : public aBase {}; > > void aFunc(containerBase<aBase*>& aContainer){} > > void aTestFunc(){ > containerBase<aDerived*> lContainer; > aFunc(lContainer); > } > > The compiler (VC 2005) complains containerBase<aDerived*> can't be > converted to containerBase<aBase*>. > > But i can't see a reason why. For any class template C, there is no inheritance relationship between two instantiations C<T1> and C<T2> regardless of the relationships between the template parameters themselves. C<T1> and C<T2> could be as different as night and day, considering the class could have specializations, etc. Thus, containerBase<T1*> and containerBase<T2*> are completely unrelated types, exacpt that they were instantiated from the same template. Besides, if what you wanted to do were actually allowed, you'd easily be able to break the type system! Consider: template<class T> class containerBase { }; class aBase {}; class aDerived : public aBase {}; class aDerived2 : public aBase {}; void aFunc(containerBase<aBase*>& aContainer { aContainer.insert(new aDerived2); } void aTestFunc(){ containerBase<aDerived*> lContainer; aFunc(lContainer); } In this case, it's really a container holding aDerived*, but if you could convert the container to hold the base type, you'd be able to insert other types that also derive from aBase, even if they have no other relationship to aDerived. Thus, your lContainer would have invalid objects in it. So it's a good thing the compiler doesn't allow this. > I could do some brute force casting but i'd prefer some more elegant > way. Is there some common pattern for it? Don't cast. Perhaps you could do something like this: template <typename T> void aFunc(containerBase<T*>& aContainer { } You could even do a static_assert that T inherits from aBase, if you care about that. Chris -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Paul Bibbings on 19 May 2010 07:37 Robi-Wan-Kenobi <robert.kauth(a)gmx.de> writes: > Hi all, > > the following code isn't compiling: > > template<class T> > class containerBase > { > > }; > > class aBase > { > > }; > > class aDerived : public aBase > { > > }; > > void aFunc(containerBase<aBase*>& aContainer) > { > > } > > void aTestFunc() > { > containerBase<aDerived*> lContainer; > aFunc(lContainer); > } > > The compiler (VC 2005) complains containerBase<aDerived*> can't be > converted to containerBase<aBase*>. > > But i can't see a reason why. Parameterized types created from templates are distinct types and, as such, any inheritance that exists between the arguments used in instantiating such types - such as is the case between your aBase and aDerived classes - is *not* transferred to a similar relationship between those instantiations. Consequently, containerBase<aBase*> and containerBase<aDerived*> remain unrelated types from the point of view of inheritance. > I could do some brute force casting but i'd prefer some more elegant > way. Is there some common pattern for it? If you adapt your classes aBase and aDerived so that they actually exhibit polymorphism, you could at least usefully populate an instance of containerBase<aBase*> with pointers to either aBase or aDerived instances, without any need to consider polymorphism of containerBase<> itself. You may then pass a containerBase<aBase*> - where the stored pointers point to instances of aDerived as the most derived class - to aFunc just as in your original attempt. Whether this will get you want you want from your idea is in the detail, of course. Regards Paul Bibbings -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Goran on 20 May 2010 12:33 On May 19, 6:32 pm, Robi-Wan-Kenobi <robert.ka...(a)gmx.de> wrote: > Hi all, > > the following code isn't compiling: > > template<class T> > class containerBase > { > > }; > > class aBase > { > > }; > > class aDerived : public aBase > { > > }; > > void aFunc(containerBase<aBase*>& aContainer) > { > > } > > void aTestFunc() > { > containerBase<aDerived*> lContainer; > aFunc(lContainer); > > } > > The compiler (VC 2005) complains containerBase<aDerived*> can't be > converted to containerBase<aBase*>. > > But i can't see a reason why. > > I could do some brute force casting but i'd prefer some more elegant > way. Is there some common pattern for it? +1 for people who explained why it's not good for your code to compile. That said... Would this do it for you: template<class T> class base_container { public: const T* get() const; T* get(); void insert(T*); // etc }; template<class T, class derivedT> class base_container_specialized { public: typedef base_container<T> this_base; private: this_base _base; public: operator const this_base&() { return _base; } const derivedT* get() const { return static_cast<const derivedT*>(_base.get()); } derivedT* get() { return static_cast<derivedT*>(_base.get()); } void insert(derivedT* p) { _base.insert(p); } // etc }; class base{}; class derived1 : public base {}; class derived2 : public base {}; base_container_specialized is NOT_A base_container. It offers same, but type-safe, interface as base_container. It does so through some simple casting (from T to derivedT). It also contains base_container, so that you can get a reference to that if need be (operator const this_base&). operator is const, to prevent e.g. calling insert with base* or derived2*, as that would be bad, as others have shown. So that gives you e.g. void f0(base_container<base>& c) { c.insert(new base); } void f0const(const base_container<base>&) {} void f1(base_container_specialized<base, derived1>&) {} void f2(base_container_specialized<base, derived2>&) {} void test() { base_container_specialized<base, derived1> c1; c1.insert(new derived1); //ok c1.insert(new derived2); //bad c1.insert(new base); //bad, don't want base among derived1 c1.insert(new derived11);//ok const base* b = c1.get();//ok, derived1 IS_A base const derived1* d1 = c1.get();//ok const derived2* d2 = c1.get();//bad, derived1 !IS_A derived2 f0const(c1);//ok, conversion operator used f0(c1);//bad, could insert base among derived1 f1(c1);//ok, it's us f2(c1);//bad, f2 wants derived2 container } I would guess that one could also come up with convenient utility to convert any base_container_specialized<derivedY, derivedZ> where derivedZ derives from derivedY, to base_container_specialized<derivedX, derivedY>, where derivedX derives from derivedY. Goran. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Joshua Maurice on 20 May 2010 12:31
On May 19, 9:32 am, Robi-Wan-Kenobi <robert.ka...(a)gmx.de> wrote: > Hi all, > > the following code isn't compiling: > > template<class T> > class containerBase > { > > }; > > class aBase > { > > }; > > class aDerived : public aBase > { > > }; > > void aFunc(containerBase<aBase*>& aContainer) > { > > } > > void aTestFunc() > { > containerBase<aDerived*> lContainer; > aFunc(lContainer); > > } > > The compiler (VC 2005) complains containerBase<aDerived*> can't be > converted to containerBase<aBase*>. > > But i can't see a reason why. > > I could do some brute force casting but i'd prefer some more elegant > way. Is there some common pattern for it? This is (partially) answered in the FAQ. See http://www.parashift.com/c++-faq-lite/proper-inheritance.html -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |