简体   繁体   中英

C++ Passing a derived class as a base class template parameter

I'm having trouble passing a class DerivedObject (part of class DerivedClass derived from template class BaseClass ) derived from BaseObject (part of template class BaseClass ) as a template argument to template class BaseClass .

This way, both Base and Derived classes have the access to the object pool, that can contain derived objects. This sounds a bit confusing, so here is the example:

template <class TDerivedClass, class TDerivedObject>
class BaseClass
{
protected:
    class BaseObject
    {
        // Class implementation
    }
    void foo() 
    { 
        static_cast<TDerivedClass*>(this)->foo(); 
    }
    std::vector<TDerivedObject*> m_objectPool;
};

The above is the base class implementation.

error C2065: 'DerivedObject' undeclared identifier

The above error is cause by the first line of the class definition bellow:

class DerivedClass : public BaseClass<DerivedClass, DerivedClass::DerivedObject>
{
protected:
    class DerivedObject : public BaseObject
    {
        // Class implementation
    }
    void foo()
    {
        // Method implementation
    }
};

Is there a way to do this? If not, is there a better solution that would give me the same / similar functionality?

At this point

class DerivedClass : public BaseClass<DerivedClass, DerivedClass::DerivedObject>

The compiler has not seen DerivedClass::DerivedObject so you get an undeclared identifier error. As they type has not been seen you cannot use it as a template parameter. You don't get one for DerivedClass as you already have declared DerivedClass as a class .

You could change you base class and have it store a std::vector<BaseObject*> in it and if you do that then you can change your code to:

template <class TDerivedClass>
class BaseClass
{
protected:
    class BaseObject
    {
        // Class implementation
    };
    void foo() 
    { 
        static_cast<TDerivedClass*>(this)->foo(); 
    }
    std::vector<BaseObject*> m_objectPool;
};

class DerivedClass : public BaseClass<DerivedClass>
{
protected:
    class DerivedObject : public BaseObject
    {
        // Class implementation
    };
    void foo()
    {
        // Method implementation
    }
};

From your example code, I get the impression that you want to provide different implementations for different base classes. Is there any special reason for using templates? If not, you could use classic polymorphism instead:

class BaseClass
{
  class BaseObject {};
  virtual ~BaseClass() {} // <- do not forget to provide virtual dtor!
  virtual void foo() = 0;
  std::vector<BaseObject*> m_objectPool;
};
class DerivedClass : public BaseClass
{
  class DerivedObject : public BaseObject {/*...*/};
  virtual void foo(){/*...*/}
};

Again, BaseObject would offer virtual or pure virtual functions - as you need.

One thing, however, you lose this way: the guarantee that in the vector there are always the objects of one specific BaseObject sub-type. If this matters for you, you could protect the pool and only allow to add new BaseObjects via DerivedClass. If this is not applicable, I might think up another solution from within BaseClass.

Here is one way of doing something similar to what was requested:

#include <vector>

using std::vector;

template <class TDerivedClass, class TDerivedObject>
class BaseClass
{
public:
    class BaseObject
    {
        // Class implementation                                                                                                                                                                                                              
    };

protected:
    // void foo()                                                                                                                                                                                                                            
    // {                                                                                                                                                                                                                                     
    //     static_cast<TDerivedClass*>(this)->foo();                                                                                                                                                                                         
    // }                                                                                                                                                                                                                                     
    // std::vector<TDerivedObject*> m_objectPool;                                                                                                                                                                                            
};

class DerivedClass;
class DerivedObject : public BaseClass<DerivedClass, DerivedObject>::BaseObject
{
    // Class implementation                                                                                                                                                                                                                  
};

class DerivedClass : public BaseClass<DerivedClass, DerivedObject>
{
public:
    void foo()
    {
        // Method implementation                                                                                                                                                                                                             
    }
};

If you do not mind having the DerivedObject structure separately from the DerivedClass you may do something like that:

template <class T>                                                                                                                                           
struct ObjectTypeTrait                                                                                                                                       
{                                                                                                                                                            
   static_assert(sizeof(T) == 0, "undefined trait");                                                                                                                      
};

template <class TDerivedClass>                                                                                                                               
class BaseClass                                                                                                                                              
{                                                                                                                                                            
protected:                                                                                                                                                   
class BaseObject                                                                                                                                         
{                                                                                                                                                        
  // Class implementation                                                                                                                                
};                                                                                                                                                       

void foo()                                                                                                                                             
{                                                                                                                                                          
   static_cast<TDerivedClass*>(this)->foo();                                                                                                                
}                                                                                                                                                          
std::vector<typename ObjectTypeTrait<TDerivedClass>::obj*> m_objectPool;                                                                                   
};

class DerivedClass;                                                                                                                                          

class DerivedObject                                                                                                                                        
{                                                                                                                                                          
   // Class implementation                                                                                                                                  
};                                                                                                                                                         


template<>                                                                                                                                                   
struct  ObjectTypeTrait<DerivedClass>                                                                                                                        
{                                                                                                                                                           
   using obj = DerivedObject;                                                                                                                                
};                                                                                                                                                          


class DerivedClass : public BaseClass<DerivedClass>                                                                                                          
{                                                                                                                                                            
protected:                                                                                                                                                   


    void foo()                                                                                                                                                 
    {                                                                                                                                                          
      // Method implementation                                                                                                                                 
    }                                                                                                                                                          
 };

I do not claim this to be very brilliant solution, but you can get the idea - using type traits or typedefs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM