Mike Lischke
Mike Lischke

Reputation: 53307

How to forward declare templated type that should belong to a class?

Assume I have 2 classes:

class A
{
public:
  typedef std::shared_ptr<A> Ref;
  ...

private:
  B::Ref _b;
}

class B
{
public:
  typedef std::shared_ptr<B> Ref;
  ...

private:
  A::Ref _a;
}

This obviously requires forward declaration of class B and B::Ref. Forward declaring B is simple, but how to do that for B::Ref too?

Upvotes: 10

Views: 1754

Answers (5)

TobiMcNamobi
TobiMcNamobi

Reputation: 4813

When I feel like forwarding a typedef, I always consider inheritance. It could look like this:

template<typename T>
class Ref : public std::shared_ptr<T>
{
    Ref()
    {}
    Ref(T *t)
        : std::shared_ptr<T>(t)
    {}
};

class B;

class A
{
public:
    //...

private:
    Ref<B> _b;
};

class B
{
public:
    //...

private:
    Ref<A> _a;
};

Upvotes: 1

lisu
lisu

Reputation: 2263

Unfortunately, you cannot forward declare nested typedef. However, you could just use global typedefs, like

typedef std::shared_ptr<B> RefB ;

and so on. Other solution is to use late template specialization, sth like the following:

template <typename T> class BImpl;

template <typename T>
class AImpl
{
public:
    typedef std::shared_ptr<AImpl> Ref;
private:
    typename BImpl<T>::Ref _b;
};

template <typename T>
class BImpl
{
public:
    typedef std::shared_ptr<BImpl> Ref;
    typename AImpl<T>::Ref _a;
};

typedef AImpl<void> A;
typedef BImpl<void> B;

Upvotes: 1

P0W
P0W

Reputation: 47784

One of way to solve this is

class A;
class B ;

template <typename T>
struct Traits {
    typedef std::shared_ptr<T>  Ptr; 
};


class A
{
    private:
      Traits<B>::Ptr _b;
};

class B
{
    private:
      Traits<A>::Ptr _a;
};

Upvotes: 8

Having a typedef for "this is how you refer to objects X" inside class X is a bad design decision, precisely because you need the full definition of X to see its members, but you want to be able to refer to X without its full definition.

I can see two ways of solving this. One is to abandon scoping and simply call the typedef RefA, defined where the class is forward-declared:

class A;
typedef std::shared_ptr<A> RefA;

Alternatively, you can delegate the "knows how to refer" to a separate class. You could make it a class template, so that classes can still register their own preferred referring types there:

template <class T>
struct RefT
{
  typedef T *type;  // Default reference type
};

template <class T>
using Ref = typename RefT<T>::type;


class A;
template <>
struct RefT<A>
{
  typedef std::shared_ptr<A> type;
};


class B
{
private:
  Ref<A> _a;
};

Upvotes: 2

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42889

You can't forward declare a nested typedef since at the point of the forward declaration B would be an incomplete type. You can however solve your problem like below:

class B;

class A {
  std::shared_ptr<B> _b;
public:
  typedef std::shared_ptr<A> Ref;
};

class B {
  A::Ref _a;
public:
  typedef std::shared_ptr<B> Ref;
};

Upvotes: 4

Related Questions