10

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.

Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • 1
    Just an aside: I don't recommend `#pragma once`. Use the `#ifndef... #define... #endif` pattern for portability. – erip Jul 07 '16 at 12:05
  • A `typedef` does not declare a specialization. – Sam Varshavchik Jul 07 '16 at 12:09
  • Thanks for the hint. My company only uses Visual Studio. There are no portability issues. :-) – Aleph0 Jul 07 '16 at 12:09
  • If a `typedef`does not declare a specialization, how can the desired behavior be accomplished correctly? – Aleph0 Jul 07 '16 at 12:10
  • Well, for starters, we need to define what "specialization" actually means. "Specialization" means something very specific in C++. And, in the code posted here, I see no evidence that the template has actually been specialized, anywhere. This is probably an improper use of terminology. – Sam Varshavchik Jul 07 '16 at 12:12
  • I think you are right. It is more that I wanted to make `GenericMap` "concrete" with `SpecialMap`. I'm unsure about the naming. – Aleph0 Jul 07 '16 at 12:14
  • 4
    @erip, do you know of a compiler that *doesn't* support `#pragma once`? – eerorika Jul 07 '16 at 12:16
  • @user2079303 Nope, but I know of many compilers that don't _have_ to support it... all of them. – erip Jul 07 '16 at 12:25
  • 2
    @Aleph0 Using #pragma once should work on any modern compiler. Shouldn't it? – pbn Jul 07 '18 at 12:22

1 Answers1

11

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.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Many thanks. That really solves and clarifies my problem. I'll include the posted code below in a new header file `SpecialMapFwd.h`. – Aleph0 Jul 07 '16 at 12:33
  • I'm right now trying to figure out what I have to do when `GenericMap` contains the typedef `typedef C map_type`. How can I possibly forward declare `map_type`? – Aleph0 Jul 07 '16 at 13:17
  • You cannot forward declare a class member without defining the main class. – Sam Varshavchik Jul 07 '16 at 21:18
  • Yes. I think I have to accept this. Many thanks for your help. I really gained a lot of insights. – Aleph0 Jul 08 '16 at 07:03