Resolving a Circular Dependency Between Template Classes

Resolving a Circular Dependency between Template Classes

What you need to do is seperate the class declarations from the implementation. So something like

template <class T> class Foo : public Base
{
public:
Base* convert(ID) const;
}

template <class T> class Bar : public Base
{
public:
Base* convert(ID) const;
}

template <class T> Base* Foo<T>::convert(ID) const {return new Bar<T>;}
template <class T> Base* Bar<T>::convert(ID) const {return new Foo<T>;}

This way, you have complete class definitions when the functions are defined.

Template circular dependency - seperate classes in different files

However, this solution (Separating the declaration from the implementation) only works when putting both classes into one file.

No, it doesn't only work in one file. You can always construct an identical file by including sub-headers. It just requires you to do something that would be unusual with non-templates (although same technique works with all inline function definitions): You need to include a file after defining the class. Headers are not limited to being at the top of the file despite the name given to them.

So, in one file:

  • Declare StateManager
  • Define State
  • Include definition of StateManager
  • Define member functions that depend on definition of StateManager

Nothing unusual in the other file:

  • Include definition of State
  • Define StateManager and its member functions.

The end result is that including either header produces the same definitions and declarations in the required order. So, this splitting of files does in no way help with limiting the amount of re-compilation caused by modifying one of the headers.

It may be a matter of taste, but I always include definitions required by inline functions (including members of templates and template functions) after the definition of the class. That way I don't need to worry whether doing so is necessary.

Circular dependency in template classes

If I make the assumption that you wanted OtherType to contain a reference to EnclosingType<OtherType>, you can do the following:

// EnclosingType declaration
template <typename T>
class EnclosingType;

// OtherType definition
class OtherType
{
public:
EnclosingType<OtherType> & e_;
OtherType (EnclosingType<OtherType> & e) : e_(e) {}
};

// EnclosingType definition
template <typename T>
class EnclosingType
{
public:
T type_;
};

You can use the EnclosingType declaration (as opposed to definition) in OtherType since you are referencing it via a pointer or reference. EnclosingType<OtherType>'s definition needs the definition of OtherType because it includes it by value.

Template classes circular dependency issue (c++)

You can solve this problem of circular dependency by removing the #include "Listener.h" from Dispatcher.h and instead adding a forward declaration for class template Listener<> as shown below:

Dispatcher.h

#pragma once

#include <vector>
//no need to include Listener.h

//forward declaration
template<typename T> class Listener;

template <typename T>
class Dispatcher {
private:
std::vector<Listener<T>*> m_listeners;

public:
Dispatcher() {}
~Dispatcher() {
for (Listener<T>* l : m_listeners) {
delete l;
}
}

void Attach(Listener<T>* listener) {
m_listeners.push_back(listener);
}

void Detach(Listener<T>* listener) {
std::vector<Listener<T>*>::iterator it = std::find(m_listeners.begin(), m_listeners.end(), listener);
if (it != m_listeners.end()) {
m_listeners.erase(it);
}
}

void Notify(T& event) {
for (Listener<T>* l : m_listeners) {
l->OnEvent(event);
}
}

};

Resolving circular dependency between concept and constrained template function

To my surprise, std::vector<int> is not considered Printable, even though operator<< works on it.

It doesn't. Not really anyway. When you say std::cout << x; works what you really mean is that you can write that expression from wherever and that works - "from wherever" including in the definition of Printable. And in the definition of Printable, it... doesn't work. Unqualified lookup doesn't find it and argument-dependent lookup doesn't find it either. The same will likely be true in most other contexts, unless you carefully add a using operator<<; in the appropriate place(s).

You could attempt to move the declaration of operator<< forward, so that the concept definition can see it - but that still wouldn't ultimately resolve the issue of any other code actually being able to call that operator. It can't really work unless this operator is declared in namespace std. And you're not allowed to add it in there.

But if you could, then this would work fine:

namespace std {
template <class T>
concept Printable = requires (ostream os, T const var) { os << var; }

template <Printable T>
ostream& operator<<(ostream&, vector<T> const&) { ... }
}

Or just use {fmt}

Resolve circular C++ template dependency

I want to do this because A has a callback to B, and B has a callback to A. For example, A would call B.do_work(), and eventually B would call A.done_work().

Ideally you'd avoid the interdepencies in the first place, e.g.:

template<class T> struct A {
std::function<void (T)> handler;
void f(T) {}
void work() { handler(T()); }
};
template<class T> struct B {
std::function<void (T)> fn;
void f(T) {}
void work() { handler(T()); }
};

// ..
A<int> a;
B<int> b;
a.handler = std::bind(&B::f, &b);
b.handler = std::bind(&A::f, &a);


Related Topics



Leave a reply



Submit