How to forward declare a C++ template class?

Solution 1:

This is how you would do it:

template<typename Type, typename IDType=typename Type::IDType>
class Mappings;

template<typename Type, typename IDType>
class Mappings
{
public:
    ...
    Type valueFor(const IDType& id) { // return value }
    ...
};

Note that the default is in the forward declaration and not in the actual definition.

Solution 2:

You can declare a templated class whose definition states the default arguments, but any time you reference the class you must include all its arguments until the definition is introduced.

eg. Let's use std::vector without including it (the second argument of std::vector is defined with a default):

namespace std
{
    template<typename, typename>
    class vector;
}

#include <iostream>

template <typename S, typename T>
void Foo (const std::vector<S,T> & vector)
{
    std::cout << "do vector stuff, eg., display size = "
        << vector.size() << std::endl;
}

template <typename T>
void Foo (const T & t)
{
    std::cout << "do non-vector stuff..." << std::endl;
}

We can then use it without including the vector, eg.:

int main ()
{
    Foo(3);
}

...Or we can use it with std::vector, eg.:

#include <vector>

// Now the compiler understands how to handle
// std::vector with one argument
// (making use of its default argument)

int main ()
{
    Foo(std::vector<int>(3));
}

I haven't checked the standards, but this works on clang/gcc with -std=c++98 up to -std=c++17, so if it's not officially a standard then it looks to be unofficially so.

Solution 3:

You can declare default arguments for a template only for the first declaration of the template. If you want allow users to forward declare a class template, you should provide a forwarding header. If you want to forward declare someone else's class template using defaults, you are out of luck!

Solution 4:

My answer complements the others as the solution I found actually mitigates the need for a template class forward declaration by creating a new type when all parameters are known (or provided as default) so that this new type, than you can forward declare, is not a template anymore:

template<typename Type=MyDefault, typename IDType=typename Type::IDType>
class MappingsGeneric
{
...
};

class Mappings : public MappingsGeneric<> {};

You can then class Mappings;. I know that this solution doesn't apply everywhere but it did in my use case as I only used templates for high-performance dependency injection for non-virtual methods in a unit test context.