8

I'm developing a cross-platform library using C++. MSVC compiles fine but g++ is giving me problems. Suppose I have the following Enum helper class:

// File: Enum.h
#ifndef ENUM_H
#define ENUM_H

#include <map>
#include <cstring>
namespace MyLib {

#define DECLARE_ENUM( type ) template<> std::map<const char*, type>  \
            MyLib::Enum<type>::mMap = std::map<const char*, type>(); \
            template<> MyLib::Enum<type>::Enum (void)

template <typename Type> class Enum
{
private:
    Enum (void);

public:
    static int Size (void) { /* ... */ return 0; }

private:
    static std::map<const char*, Type> mMap;
};

}
#endif

Here is the intended use:

// SomeFile.cpp
#include "Enum.h"

enum MyEnum
{
    value1, value2, value3,
};

DECLARE_ENUM (MyEnum)
{
    mMap["value1"] = value1;
    mMap["value2"] = value2;
    mMap["value3"] = value3;
}

void SomeFunc (void)
{
    cout << Enum<MyEnum>::Size();
}

g++ gives me a "Specialization of template in different namespace" error. Wrapping the DECLARE_ENUM block in namespace MyLib solves this problem. My question is why do I have to do this and is there another way of fixing this that doesn't require me to add a namespace MyLib around the block?

Dave
  • 7,283
  • 12
  • 55
  • 101
  • Don't use macros, especially not in header files. – Robert Allan Hennigan Leahy Aug 14 '14 at 15:11
  • possible duplicate of [Why aren't template specializations allowed to be in different namespaces?](http://stackoverflow.com/questions/3072248/why-arent-template-specializations-allowed-to-be-in-different-namespaces) – Robert Allan Hennigan Leahy Aug 14 '14 at 15:18
  • @RobertAllanHenniganLeahy It's different here. Note that this macro specifies `MyLib::`, and it explicitly specializes a *member* of an implicit instantiation. In fact, clang [accepts](http://coliru.stacked-crooked.com/a/d6a5b8464a4920c5) this code modulo typos. – T.C. Aug 14 '14 at 15:23
  • [Here's yet another similar question](http://stackoverflow.com/questions/25594644/warning-specialization-of-template-in-different-namespace) that may interest readers here. – nonsensickle Apr 27 '17 at 10:37

1 Answers1

8

This changed in C++11 as a result of CWG issue 374 and N3064. The current wording (§14.7.3 [temp.expl.spec]/p2) is:

An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (7.3.1), any namespace from its enclosing namespace set.

Since your declarator-id is in fact qualified with MyLib::, and the global namespace is a "namespace enclosing the specialized template", this looks like a GCC bug (bug 56480). Your code compiles fine with clang in C++11 mode.

In C++98, however, the specialization must be put inside namespace in which the template is a member (see Mark B's comment below), and clang will produce a warning if put in C++98 mode.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • This appears to have changed since C++98 where it says `An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member.`. So depending on which standard is in use would indicate which compiler is wrong. (The OP doesn't indicate C++11 explicitly or via tag). – Mark B Aug 14 '14 at 15:59
  • @MarkB trailing commas at the end of enumerator lists is a C++11 thing, though :) Clarified anyway. – T.C. Aug 14 '14 at 16:40
  • @MarkB I am using C++11, sorry I didn't specify this earlier. – Dave Aug 14 '14 at 20:03