Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 790
Aleph0 Avatar asked Jul 07 '16 12:07

Aleph0


People also ask

What is the forward declaration of classes?

Forward Declaration refers to the beforehand declaration of the syntax or signature of an identifier, variable, function, class, etc. prior to its usage (done later in the program). In C++, Forward declarations are usually used for Classes.

Can you forward declare a template?

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! Save this answer.

How do you forward a function declaration?

To write a forward declaration for a function, we use a function declaration statement (also called a function prototype). The function declaration consists of the function header (the function's return type, name, and parameter types), terminated with a semicolon. The function body is not included in the declaration.

Where do you put forward declaration?

Generally you would include forward declarations in a header file and then include that header file in the same way that iostream is included.


1 Answers

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.

like image 152
Sam Varshavchik Avatar answered Oct 18 '22 08:10

Sam Varshavchik