How to forward declare a C++ template class?

C++TemplatesForward Declaration

C++ Problem Overview


Given a template class like the following:

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

How can someone forward declare this class in a header file?

C++ Solutions


Solution 1 - C++

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 - C++

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 struct Foo without including it:

template <class>
struct Foo;

// Note that we *must* use the template here,
// even though in the definition it'll have a default.
template <class T> 
auto Func (Foo<T> foo)
{
    // do stuff with Foo before it's defined:
    return foo.Bar();
}

int Func (int n)
{
    return n;
}

We can then compile it without including the definition, eg.:

int main ()
{
    return Func(3);
}

demo

...Or we can use it after including the definition, eg.:

template <class T = bool>
struct Foo
{
    T Bar () {return 9;}
};

// Now the compiler understands how to handle
// Foo with no template arguments
// (making use of its default argument)

int main ()
{
    return Func(Foo<>());
}

demo

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.


Although in principal this should work for namespace std, and appears to in the examples I've checked (with many compilers), the standard states that it's undefined behaviour: According to the C++11 standard, 17.6.4.2.1:

> The behavior of a C++ program is undefined if it adds declarations or > definitions to namespace std or to a namespace within namespace std > unless otherwise specified.

(I got this info from an SO answer).

Thanks to Antonio for pointing this out in the comments (and providing the link).

Solution 3 - C++

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 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTron ThomasView Question on Stackoverflow
Solution 1 - C++PubbyView Answer on Stackoverflow
Solution 2 - C++ElliottView Answer on Stackoverflow
Solution 3 - C++Dietmar KühlView Answer on Stackoverflow
Solution 4 - C++Roland SarrazinView Answer on Stackoverflow