3

I have been studying the CRTP lately and have come up of an idea to create a generic base template class using CRTP.

// Example.h
namespace A {
    template <class TClass, typename T>
    class Example {
    public:
        Example(T &someStruct) : m_someStruct_(someStruct)
        {
        }

        ~Example() 
        {
            DoThis();
        }

    public:
        void DoThis()
        {
            static_cast<TClass*>(this)->DoThat(m_someStruct_);
        }

    private:
        T m_someStruct_;
    };
}

// AsArgument.h
namespace A {
    class AsArgument : public Example <AsArgument, SomeStruct> {
    friend class Example <AsArgument, SomeStruct>;
    private:
        void DoThat(SomeStruct &someFun)
        {
            // Do something to someFun object.
            // yehey(someFun);
            printf("I want to do that! \n");
        }
    };
}

My goal is to use the base class object to access the derived class' functions and at the same time, separate the base and derived's implementation by including only the base class' header file and forward declare the derived class as a template argument.

I know the basics of what I can do with incomplete types but I can't seem to find information about templates.

Is it valid to forward declare the class TDerived argument instead of including the header file?

// SomeFile.cpp
#include "Example.h"

class A::AsArgument; // Forward declare this instead of including the AsArgument.h header file

namespace B {
    void SomeClass::DoSomething()
    {
        SomeStruct fun;
        Example <AsArgument, SomeStruct> example(fun);
    }
}

I am not sure if this is a good design in creating a generic base template class, but my goal after establishing the base class is to easily create derived classes out of it and define the base class implementation on compile time. It's actually some sort of a combination of RAII and CRTP.

I can actually achieve this by including the "AsArgument.h" file with "Example.h" but the separation between the base and the implementation is lost. And I keep getting compile error when I try to forward declare the AsArgument class (probably because of namespace issues I'm not fully aware of).

Any advise or is this kind of design even efficient and valid?

2
  • Why do you need to derive at all? Your toy example doesn't seem to need it. Commented Jul 1, 2015 at 3:30
  • I wanted to make some generic base class and have the implementation on each of the derived class, although It's not necessary, I'm curious if it can be done or if it's possible. Commented Jul 1, 2015 at 5:13

1 Answer 1

1

I'm not really sure what the design goal is here, but the rules about incomplete types apply in the same way whether or not you're talking about templates, you just need to think about where the template is instantiated.

In your case, you are trying to avoid including AsArgument.h in SomeFile.cpp. However, you instantiate the Example class template with the AsArgument class. That means that when you compile SomeFile.cpp, that translation unit does not know anything about the AsArgument class (because it doesn't see its declarations in the .h file), only that it exists.

However, as you might expect, you can't do much with a class if you only know it exists. You can't even hold it by value, since you don't know its size. You can't use any of its interface. In your example, there is no way the compiler can know that AsArgument::DoThat even exists (it doesn't necessarily need to know what it does, that can be left for the linker). Remember that Example is being instantiated in SomeFile.cpp, so that's where the compiler will need to know that DoThat exists.

So you need AsArgument.h. With a normal class, you could just put the declaration in the .h file, and put the definitions (the implementation) in the .cpp file. But AsArgument is a template class, so you can't do that in general. You can only do this for templates if you are templated on a limited number of classes, known in advance, and are willing to explicitly template on all of them.

I can't comment too much on the bigger picture, because I don't know what you are trying to do. I'm not very confident that CRTP is even a fit for you. CRTP is useful for certain things but it's not really the first tool that I turn to. Actually, now that I think of it: I quite rarely use it. In most cases if I'm going to use template based polymorphism, I could just hold the "child" directly and skip the base entirely, in many cases I don't feel like the base buys me enough.

I'd recommend including any compiler errors you have in future SO questions. Good luck!

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the explanation about forward declaration on templates. I now understood why I have include AsArgument.h.
As for my design goal. I wanted to make a generic base class, use it on some other class and execute the implementation of each derived (thus the static_cast to derived). I'm actually trying to experiment some stuff using CRTP. Although I can actually achieve this without the template base class, I'm curious if this could be plausible. I want to upvote your answer but I can't because my reputation is below 15. I'll upvote you once I reach 15 reputation :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.