不同命名空间中模板的特化

8

我正在使用C++编写一个跨平台库。MSVC编译很好,但是g++却出了问题。假设我有以下的枚举帮助类:

// 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

这是预期的使用方式:

// 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++编译时,我遇到了“在不同命名空间中特化模板”的错误。将DECLARE_ENUM块包装在MyLib命名空间中可以解决此问题。我的问题是为什么我必须这样做,是否有另一种修复方法,不需要在块周围添加MyLib命名空间?


不要使用宏,尤其是在头文件中。 - Robert Allan Hennigan Leahy
可能是重复的问题:为什么模板特化不允许在不同的命名空间中?(https://dev59.com/YHA75IYBdhLWcg3w186G) - Robert Allan Hennigan Leahy
@RobertAllanHenniganLeahy 这里有所不同。请注意,此宏指定了 MyLib::,并且明确地专门化了隐式实例化的成员。事实上,clang 接受这段代码,除了打字错误。 - T.C.
这里有另一个类似的问题(https://dev59.com/sF8e5IYBdhLWcg3wlbLx),可能会引起读者们的兴趣。 - nonsensickle
1个回答

8
这是因为CWG issue 374N3064,C++11中已有所改变。当前的措辞 (§14.7.3 [temp.expl.spec]/p2) 是:

显式特化应在封装该特化模板的命名空间中声明。如果其declarator-id没有限定符,则应在最近的包含该模板的命名空间中声明,或者(如果该命名空间是内联的(7.3.1))则可以从其封闭命名空间集合中选择任意命名空间。

由于您的declarator-id实际上是用MyLib::限定的,并且全局命名空间是“封装特化模板的命名空间”,所以这看起来像是一个GCC的 bug (bug 56480)。使用C++11模式的clang编译您的代码可以正常工作。
然而,在C++98中,特化必须放在模板作为成员的命名空间中(请查看下面Mark B的评论),如果放在C++98模式下,clang将产生警告。

这似乎与C++98有所改变,其中它说“显式特化应在模板所属的命名空间中声明,或者对于成员模板,在封闭类或封闭类模板所属的命名空间中声明。”因此,取决于使用哪个标准将指示哪个编译器是错误的。(OP没有明确指出C++11或通过标签)。 - Mark B
@MarkB 在枚举列表的末尾使用逗号,这是C++11的一种用法。虽然如此,还是进行了澄清。 - T.C.
@MarkB 我正在使用C++11,之前很抱歉我没有说明。 - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接