Sasha
Sasha

Reputation: 856

Inner class template circular reference

Does anyone knows how you can trick the C++ compiler to compile something like that (with the condition that TheObservedObject remains inside MyClass ):

template< typename Type >
class Observabile
{
public:
   typename typedef Type::TheObservedObject TheObject;

   void Observe( TheObject& obj ) {}
};

class MyClass : public Observabile< MyClass >
{
public:
   class TheObservedObject
   {
   };
}

Upvotes: 5

Views: 575

Answers (3)

jakar
jakar

Reputation: 1061

As others have said, MyClass is incomplete at the point where Observable is instantiated. However, you could use a traits class. Something like this might work:

template<class T>
struct ObservedObject {};

template<class T>
class Observable
{
public:
    typedef typename ObservedObject<T>::type TheObject;

    void Observe(TheObject& obj) {}
};

class MyClass;  // forward decl

class MyClassObservedObject
{
    // define outside of MyClass
};

template<> struct ObservedObject<MyClass>
{
    typedef MyClassObservedObject type;
};

class MyClass : public Observable<MyClass>
{
    //
    // ...
    //

private:
    friend class MyClassObservedObject;
};

Upvotes: 1

iammilind
iammilind

Reputation: 70020

Neither you can forward declare the inner class nor you can access them as incomplete type from the template Observable. However, in such case, you can have one trick.

// make inner class external
class TheObservedObject
{
  private: // make everything private
  friend class MyClass;  // make friends with original outer class
};

Thus, effectively TheObservedObject is accessible only for MyClass.

Now you can change class Observabile<> accepting 2 parameters and pass the above class also. This might impose certain limitations, but it can be a close match.

Upvotes: 1

Xeo
Xeo

Reputation: 131829

Sadly, that is not directly possible, as MyClass, at the point of instantiation of Observable, is not yet complete and as such you can't access any typedefs. You can work around this by adding a small wrapper:

template< typename Type, typename Wrapper >
class Observable
{
public:
   typename typedef Wrapper::TheObservedObject TheObject;

   void Observe( TheObject& obj ) {}
};

struct MyClassWrapper{
   class TheObservedObject
   {
   };
};

class MyClass : public Observable< MyClass, MyClassWrapper>
{
public:
    typedef MyClassWrapper::TheObservedObject TheObservedObject;
};

Or generelly just put TheObservedObject outside of MyClass(Wrapper).

Upvotes: 1

Related Questions