在另一个命名空间中定义符号

时间:2009-09-24 09:33:32

标签: c++ templates namespaces

这是我的问题:在标题中我定义了一个结构模板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<T>::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,以便每个命名空间都有自己的版本,或类似的东西)?< / p>

2 个答案:

答案 0 :(得分:9)

根据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 :(得分:1)

这是我使用的解决方案,使用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次字符'}',如果有人有更优雅的方式这样做,随时发表评论!