【问题标题】:Define a symbol in another namespace在另一个命名空间中定义符号
【发布时间】:2009-09-24 09:33:31
【问题描述】:

这是我的问题:在标题中我定义了一个结构模板type_to_string,它旨在定义一个与给定类型参数对应的字符串:

namespace foo {

    template <typename T>
    struct type_to_string
    {
        static const char * value;
    };
}

template <typename T>
const char * foo::type_to_string<T>::value = "???";

我还为字符串定义了一个默认值。

现在,我想使用宏来定义新类型:

#define CREATE_ID(name)                               \
struct name;                                          \
                                                      \
template<>                                            \
const char * foo::type_to_string<name>::value = #name;

问题是我希望宏可以在命名空间中使用,例如:

namespace bar
{
    CREATE_ID(baz)
}

这是不可能的,因为type_to_string&lt;T&gt;::value 必须定义在包含foo 的命名空间中。

这是我得到的编译错误:

[COMEAU 4.3.10.1] error: member "foo::type_to_string<T>::value [with T=bar::baz]"
cannot be specialized in the current scope

[VISUAL C++ 2008] error C2888: 'const char *foo::type_to_string<T>::value' :
symbol cannot be defined within namespace 'bar'
     with
     [
         T=bar::baz
     ]

奇怪的是,GCC 4.3.5(MinGW 版本)不会产生任何错误。

有没有人知道解决方法,可能是通过使用一些我不知道的查找规则(即在宏中声明 type_to_string 以便每个命名空间都有自己的版本,或类似的东西)?

【问题讨论】:

    标签: c++ templates namespaces


    【解决方案1】:

    根据 C++ 标准 14.7.3/2:

    应在模板所属的命名空间中声明显式特化,或者,对于 成员模板,在其封闭类或封闭类模板是其成员的命名空间中。 类模板的成员函数、成员类或静态数据成员的显式特化 应在类模板所属的命名空间中声明。这样的声明也可以 是一个定义。如果声明不是定义,则可以稍后在名称中定义特化- 显式特化被声明的空间,或者包含在其中的名称空间中 已声明显式特化。

    你可以这样写:

    #define DECL_ID(name) \
    struct name;                                          
    
    #define CREATE_ID(name) \
    template<>              \
    const char * foo::type_to_string<name>::value = #name;
    
    namespace bar { namespace bar2 {
        DECL_ID(baz)
    } }
    CREATE_ID(bar::bar2::baz)
    

    或者

    #define CREATE_ID(ns, name)     \
    namespace ns { struct name; }   \
                                    \
    template<>                      \
    const char * foo::type_to_string<ns::name>::value = #name;
    
    CREATE_ID(bar, baz)
    

    第三个选项是前两个的叠加。它允许在value 中使用非限定名称(如果需要):

    #define DECL_ID(name) \
    struct name;                                          
    
    #define CREATE_ID(ns, name) \
    template<>              \
    const char * foo::type_to_string<ns::name>::value = #name;
    
    namespace bar { namespace bar2 {
        DECL_ID(baz)
    } }
    CREATE_ID(bar::bar2, baz)
    

    【讨论】:

    • 感谢标准中的段落,有确切的要求总是好的。关于您的第二个解决方案,实际上我已经在做:我有两个参数,一个用于定义命名空间,另一个用于类型。但是,我希望宏可以在另一个命名空间中使用,以免限制使用的命名空间的数量。我可以使用 1, 2, 3, ... 参数定义 CREATE_ID 宏来接受多个命名空间名称,但这会相当乏味。如果这是唯一的解决方案,我将没有太多选择……
    • 为了记录,我最终使用 Boost.Preprocessor 将命名空间列表传递给宏。这样,名称可以包含在多个嵌套的命名空间中。
    【解决方案2】:

    这是我采用的解决方案,使用 Boost.Preprocessor:

    #include <boost/preprocessor/seq/for_each.hpp>
    #include <boost/preprocessor/seq/size.hpp>
    #include <boost/preprocessor/repetition/repeat.hpp>
    
    #define BEGIN_NS(r, _, elem)  namespace elem {
    #define CLOSE_NS(z, n, _)     }
    #define APPEND_NS(r, _, elem) elem::
    
    #define CREATE_ID(ns_list, name)                         \
                                                             \
    BOOST_PP_SEQ_FOR_EACH(BEGIN_NS, ~, ns_list)              \
        struct name;                                         \
    BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(ns_list), CLOSE_NS, ~) \
                                                             \
    template<>                                               \
    const char * devs::type_to_string<                       \
        BOOST_PP_SEQ_FOR_EACH(APPEND_NS, ~, ns_list)name     \
    >::value = #name;
    

    必须在这样的任何命名空间之外使用:

    CREATE_ID((bar) (bar2), baz)
    

    我必须定义一个宏来重复 n 次字符'}',这似乎很奇怪,如果有人有更优雅的方法,请随时发表评论!

    【讨论】:

      猜你喜欢
      • 2021-11-10
      • 1970-01-01
      • 2010-10-12
      • 2011-01-13
      • 2012-05-27
      • 2011-01-15
      • 2020-09-21
      • 1970-01-01
      • 2013-11-19
      相关资源
      最近更新 更多