André Pareis
André Pareis

Reputation: 1094

Self-referencing Template in Template Argument

What can I do to make this work:

template<class C, class V, Test V::*>
class Test {
};

it gives me compiler error:

unknown type name 'Test'

It's a self-referencing template for now, which doesn't seem possible. What could possibly be done to make it work?

EDIT:

Here's what I'd need this for. I want to implement a bi-directional (think parent-child) relationship schema with the least minimum coding effort.

template <class O, class T, Reference<T, O, Reference O::*> T::* opposite>
class Reference
{
    T **data;
    int count;
public:
    Reference(): data(new T*[N]), count(0) {}
    ~Reference() {delete[] data;}
    Reference &add(T *t) {
        handleOtherSide();
        return link(t);
    }
    // a lot of stuff to implement this
};

That's the collection class. Here's how it would be used:

class Partner
{
public:
    Reference<Partner, Address, &Address::partner> addresses;
};

class Address
{
public:
    Reference<Address, Partner, &Partner::addresses> partner;
};

My goal is to have everything necessary for Reference to work be supplied as template argument, so that there is no need to provide constructors for classes like Partner and Address (currently I supply the opposite member pointer as a constructor arg but this requires me to have explicit constructors for the participant classes). I would also need to pass in or calculate an "owner" pointer to the Reference class. I left this problem out here because I want to focus on the self-referencing template aspect.

The easiest way to think of this is boost::bimap. But the problem with bimap is that I don't want the enclosing bimap but just the left and right part of it. bimap is also not feasible because it would lead to one single bimap managing all associations of a specific relationship. It would possibly hold a large number of objects slowing down operations on it.

Upvotes: 7

Views: 2806

Answers (2)

Andr&#233; Pareis
Andr&#233; Pareis

Reputation: 1094

The problem is, what I want to achieve is not possible in C++, at least not with templates and the amount of code and classes I am aiming at (read: single line of code per member). It starts with the compiler needing forward declarations and fully defined types, which is making by-value members and template arguments of such impossible (in case of cyclic dependencies). Then it is not possible to have a member pointer as a template arg when the class of that member is not yet fully defined. The root cause of all this is how the compiler works: it is single pass. And there is nothing I can do about that.

The solution is to use by-reference members or an OO style base class or a boost::any style container to avoid the templates. With the latter 2 it might be possible to have by-value members.

Upvotes: 1

lapk
lapk

Reputation: 3918

Are you looking for something like this? It's not self-referencing template, but you can specify derived class as a template type for base class and base class can call derived methods etc.:

template< typename PType, typename PDerived >
class TBase
{
  //do stuff with TDerived
 public:
  bool foo( void )
  {
   return ( static_cast< PDerived* > ( this )->bar() );
  }
};

template< typename PType >
class TDerived : public TBase< PType, TDerived< PType > >
{
  friend class TBase< PType, TDerived< PType > > ;
  //do stuff
 protected:
  bool bar( void )
  {
   return ( true );
  }
};

EDIT: Once again, I'm not sure what's your final goal. Here is a solution to what I think you want, or, at least, some hint to what you might use to implement your design. The only requirement that I put is that both TAddress and TPartner have function with same name. See if that's what you need. In principle, you can make a helper class and use CRTP to access member function through a pointer, but I don't think you actually need it.

template< typename PType1, typename PType2 >
class TReference
{
 public:
  int mFlag;

  TReference() :
   mFlag( 0 )
  {
  }
  TReference( int fFlag ) :
   mFlag( fFlag )
  {
   std::cout << "Creating reference " << PType1::sName << " -> " << PType2::sName << "." << std::endl;
  }
  TReference< PType2, PType1 > AccessOpposite( void )
  {
   PType2 lTmp;
   lTmp.Opposite();

   return TReference< PType2, PType1 > ( -1 );
  }
};

class TPartner;

class TAddress
{
 public:
  static const char* sName;
  TReference< TAddress, TPartner > mRef;

  void Opposite( void )
  {
   std::cout << sName << "::Opposite" << std::endl;
  }
};

class TPartner
{
 public:
  static const char* sName;
  TReference< TPartner, TAddress > mRef;

  TReference< TAddress, TPartner > Opposite( void )
  {
   std::cout << sName << "::Opposite" << std::endl;
  }
};

const char* TAddress::sName = "TAddress";
const char* TPartner::sName = "TPartner";

int main( void )
{
 TAddress lAddress;
 TPartner lPartner;

 std::cout << lAddress.mRef.mFlag << " " << lPartner.mRef.mFlag << std::endl;

 lPartner.mRef = lAddress.mRef.AccessOpposite();

 std::cout << lAddress.mRef.mFlag << " " << lPartner.mRef.mFlag << std::endl;

 return ( 0 );
}

Upvotes: 3

Related Questions