-1

I have a class A with the following declaration (A.h file):

#ifndef __A_DEFINED__
#define __A_DEFINED__

class A
{
public:
  template<typename T> inline void doThat() const;
};

#endif

and a class B deriving from that class (B.h file):

#ifndef __B_DEFINED__
#define __B_DEFINED__

#include <iostream>
#include "A.h"

class B : public A
{
public:
  void doThis() const { std::cout << "do this!" << std::endl; }
};

#endif

So far, so good. My issue is that the function A::doThat() uses B::doThis():

template<typename T> inline void A::doThat() const { B b; b.doThis(); }

Usually, the circular dependency would not be an issue because I would just define A::doThat() in the .cpp file. In my case however, doThat is a template function so I can't do that.

Here are the solutions I have envisioned so far:

  1. Defining the template function A::doThat() in a .cpp file. The issue with that is that I need to instantiate explicitly all the calls with various template arguments (there might be many in the real case).

  2. After the declaration of the A class in A.h, add #include "B.h" and then define the A::doThat() function. This works fine in visual studio but g++ does not like it.

Is there a neat way to solve this problem?

EDIT: In the real case, there is not just one child class B, but several (B, C, D, etc.) The function A::doThat() depends on all of them. The function B::doThis() is also templated.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
Touloudou
  • 2,079
  • 1
  • 17
  • 28
  • 2
    Unrelated: Any identifier with two underscores in a row is reserved for use by the implementation. Use them at your own risk. More information here: [What are the rules about using an underscore in a C++ identifier?](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) – user4581301 Oct 16 '17 at 22:28

4 Answers4

1

A default template parameter for the B class could work:

#include <iostream>

// include A.h
class B;

class A
{
public:
    template<typename T, typename U = B> inline void doThat() const 
    {
        U b; b.doThis();
    }
};

// include B.h
class B : public A
{
public:
    void doThis() const { std::cout << "do this!" << std::endl; }
};

// main
int main()
{
    A a;
    a.doThat<int>();
}
wally
  • 10,717
  • 5
  • 39
  • 72
  • This would work only with a default template parameter or even if define the function here? I am saying this because in the real case, doThat() depends on B, but also on children C, D and E. – Touloudou Oct 17 '17 at 12:32
  • @Touloudou That sounds like a related, but different question with a different answer. – wally Oct 17 '17 at 12:43
1

Usually the best way to allow a parent to call a child function is to declare the function as a pure virtual function in the parent and override it in the children.

#include <iostream>

class A
{
public:
    virtual ~A() = default;
    template<typename T> inline void doThat() const
    {
        // do some other stuff
        doThis();
    }
    virtual void doThis() const = 0; // pure virtual function
};

class B: public A
{
public:
    void doThis() const override
    {
        std::cout << "do this!" << std::endl;
    }
};

int main()
{
    B b;
    A* ap = &b;
    ap->doThat<int>();
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • The issue here is that the doThis() function should also be templated in the real case. It means this would be a virtual template function, which is not OK in current c++. – Touloudou Oct 17 '17 at 12:19
  • @Touloudou that's really important information to have in the question. You should edit the question and add it. If it is templated without (or with simple) parameters you can abstract the templated function behind a regular one. – user4581301 Oct 17 '17 at 15:25
0

The following does work with g++:

File A.h:

#ifndef __A_DEFINED__
#define __A_DEFINED__

class A
{
public:
  template<typename T> inline void doThat() const;
};

#include "B.h"

template<typename T> inline void A::doThat() const { B b; b.doThis(); }

#endif

File B.h:

#include <iostream>

#include "A.h"

// We check for the include guard and set it AFTER the inclusion of A.h
// to make sure that B.h is completely included from A.h again.
// Otherwise the definition of A::doThat() would cause a compiler error
// when a program includes B.h without having included A.h before.
#ifndef __B_DEFINED__
#define __B_DEFINED__

class B : public A
{
public:
  void doThis() const { std::cout << "do this!" << std::endl; }
};

#endif

File test_A.cpp:

// In this test case we directly include and use only A.
#include "A.h"
#include "A.h" // We test whether multiple inclusion causes trouble.

int main() {
    A a;
    a.doThat<int>();
}

File test_B.cpp:

// In this test case we directly include and use only B.
#include "B.h"
#include "B.h" // We test whether multiple inclusion causes trouble.

int main() {
    B b;
    b.doThat<int>();
    b.doThis();
}

Alternative Idea:

I do not know whether you (or some coding conventions) insist on separate header files for each class, but if not the following should work:

You can put the definitions of class A and class B and of the member function template A::doThat<typename>() (in this order) together in one header file AandB.h (or whatever name you like).

Fabian
  • 66
  • 1
  • 6
  • @Touloudou I got your problem working with `g++` also according to your idea 2. I had to move the include guard (see the comment in B.h). – Fabian Oct 24 '17 at 16:32
0

This cries for polymorphism. There are two options using polymorphism:

  1. Dynamic polymorphism, i.e. make A an abstract base class and call doThis() virtually:

    struct A
    {
        virtual void do_this() const = 0;
        template<typename T>
        void doThat() const { doThis(); }
    };
    
    struct B : A
    {
        void doThis() const override { /* ... */ }
    };
    

    Of course, this only works if doThis() is not templated. If you need that, you could use

  2. Static polymorphism, i.e. CRTP, when

    template<typename Derived>
    struct A
    {
        template<typename T>
        void doThat() const { static_cast<const Derived*>(this)->template doThis<T>(); }
    };
    
    struct B : A<B>
    {
        template<typename T>
        void doThis() const { /* ... */ }
    };
    

If (as in your example code) B::doThis() is not called for the same object, but for some temporary, you could

template<typename typeB>
struct A
{
    template<typename T>
    void doThat() const { typeB b; b.template doThis<T>(); }
};
Walter
  • 44,150
  • 20
  • 113
  • 196