【问题标题】:Is it okay to specialize and define template static members in some random namespace?可以在某个随机命名空间中专门化和定义模板静态成员吗​​?
【发布时间】:2013-01-26 13:01:50
【问题描述】:

我有一些这样的代码:

template<> const string &Wrapper<Foo>::s_Name = "Foo";
template<> const Binding Wrapper<Foo>::s_Bindings[] = {
    Binding("m1", &caller<&Foo::f1>),
    Binding("m2", &caller<&Foo::f2>),
    Binding("m3", &caller<&Foo::f1>),
    Binding("outer", &caller<&outer>),
};
template<> const int Wrapper<Foo>::s_BindingsLength =
    (sizeof(ArraySizeHelper(s_Bindings))); 

ArraySizeHelper 在这里计算编译时的数组大小。非成员函数和成员函数可以绑定。 我写了一些宏来节省编写这些绑定的时间:

#define BIND_START(Class) \
    namespace _Bind##Class##Namespace { \
        typedef Class _BindClass; \
        template<> const string &Wrapper<_BindClass>::s_Name = #Class; \
        template<> const Binding Wrapper<_BindClass>::s_Bindings[] = {

#define BIND(FunctionName, Function) \
            Binding(FunctionName, &caller<Function>),

#define BIND_END \
        };\
        template<> const int Wrapper<_BindClass>::s_BindingsLength = \
            (sizeof(ArraySizeHelper(s_Bindings))); \
    }

现在上面的代码可以这样写:

BIND_START(Foo)
    BIND("m1", &Foo::f1)
    BIND("m2", &Foo::f2)
    BIND("m3", &Foo::f1)
    BIND("outer", &outer)
BIND_END

更容易打字和阅读。为什么我需要把它放在命名空间中?因为我找不到另一种方法来编写这些宏,即类名必须只写一次(成员函数指针除外)并将其与多个类一起使用。现在我想知道,这样做可以吗?如果没有,还有其他方法可以实现我想要的功能吗?

Full code of this example on Pastebin


这似乎是 GCC 4.7.2 中的一个错误(不确定其他版本)

此代码违反 9.4.2/2:“静态数据成员的定义应 出现在包含成员类定义的命名空间范围内。"
这是错误报告:GCC Bugzilla – Bug 56119

【问题讨论】:

    标签: c++ templates binding scripting macros


    【解决方案1】:

    C++11 标准似乎禁止您的解决方案。这就是 9.4.2/2 所说的:

    “静态数据成员在其类定义中的声明不是定义,可能是除 cv 限定的 void 以外的不完整类型。静态数据成员的定义应出现在包含成员类定义的命名空间范围"

    现在,您定义静态成员数据的命名空间(其名称是评估 _Bind##Class##Namespace 预处理器表达式的结果)确实包含定义类的命名空间(即,在你的情况,全局命名空间)。因此,静态数据成员定义是非法的。

    我不确定您使用的是什么编译器,但如果它编译了它,那么它就是一个 错误(实际上这似乎是 GCC 4.7.2 的情况)。 Clang 3.2 正确拒绝编译它并产生正确的输出消息:

    source.cpp:106:1: error: cannot define or redeclare 's_Name' here because namespace '_BindFooNamespace' does not enclose namespace 'Wrapper<Foo>' BIND_START(Foo)

    至于替代解决方案,我认为没有,只要你需要BIND_END扩展中的类名(这是s_BindingsLength的编译时初始化所需要的)。如果宏没有将类名作为参数,实际上,该名称必须作为typedef 可用(如果允许宏扩展为#define 指令,则情况会如此,但事实并非如此)。由于BIND_END 没有提示如何形成类的名称,因此它必须寻找一个始终相同的名称。如果名称必须始终相同,则必须将其放入单独的命名空间以避免名称冲突。但这是上面提到的标准规则所禁止的。

    换句话说,恐怕您必须向BIND_END 宏添加一个参数。毕竟,这还不错。

    【讨论】:

    • 感谢您的回答。是的,我使用的是 GCC 4.7.2 并且之前从标准中看到了该行,所以当我的代码编译没有错误并决定澄清这种情况时,我感到很惊讶。好吧,一个参数没什么大不了的,它在这个宏 BTW 的第一个版本中。我想我现在应该填写错误报告 :)
    • @Zhur:请这样做。如果您可以在此处的某处发布错误报告的链接,那就太好了(也许作为对您问题的编辑)。
    • 完成 - 链接在问题中。
    猜你喜欢
    • 2011-01-21
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多