Aleph0
Aleph0

Reputation: 6084

Correct forward declaration of fully specialized template classes

Assume that I have the following bunch of files:

Generic.h: Complicated template class

#pragma once

template<typename K, typename V, template<typename Key, typename Value, typename ...> typename C>
struct GenericMap
{
    C<K, V> key;
};

Special.h: Define fully specialized version of mentioned template class, simplifying the ease of use.

#pragma once

#include "Generic.h"
#include <string>
#include <map>

typedef GenericMap<std::string, int, std::map> SpecialMap;

Client.h: Client that uses SpecialMap and define forward declaration.

#pragma once

class SpecialMap; // Wrong forward declaration

struct Client {
    Client();
    SpecialMap* map;
};

Client.cpp: Clients code might know Generic.h and Special.h

#include "Client.h"
#include "Special.h"

Client::Client()
{
    map["343"] = 2;
}

main.cpp:

#include <Client.h>

int main(int argc, char**args) {
    Client c;
    return 0;
}

GenericMap represents a template class that has no forward declaration. For some users a fully specialized version SpecialMapof GenericMap should suffices, where for the ease of use a typedef is used.

Now Client uses internally SpecialMap, but the header file should only declare a forward declaration for SpecialMap.

Unfortunately, the following files will not compile. Somehow the posted forward declaration will suffice. What will be the correct one?

I'm sorry for the long listings, but it was the smallest non-working example I could think of.

Upvotes: 10

Views: 10844

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118425

In the comments you clarified that you were not actually referring to C++ specialization. You were asking merely about the typedef:

typedef GenericMap<std::string, int, std::map> SpecialMap;

And that's pretty much the end of the story. This declares SpecialMap to be a typedef, a type alias. Any translation unit that needs to use SpecialMap needs to include this type definition. And only this definition. Nothing else needs to be done. It does not need to be declared in any other way. It is an alias. A search/replace of the typedef alias with its underlying type produces the same exact results. A typedef declared in one translation unit is visible only in that translation unit. There are no short-cuts for other translation units to import the typedef into their scope.

In your Client.h:

#include <Special.h>

That's where you defined this typedef, and that's the only way to pull in this definition.

However, this may also be the case where the typedef is a part of a larger header file, and it is desirable to pull in just the typedef, separately. This could be done with a header file containing only:

#include <string>
#include <map>

template<typename K, typename V,
        template<typename Key, typename Value, typename ...>
             typename C> struct GenericMap;

typedef GenericMap<std::string, int, std::map> SpecialMap;

This would be the bare minimum needed to define the typedef alias. Anything that actually needs to use it, will need to #include not just this header file, but also your Generic.h header, which actually defines the GenericMap template class, that's only forward-declared here.

Upvotes: 11

Related Questions