声明是否有可能逃避其封闭的命名空间?

时间:2018-04-23 23:37:56

标签: c++ namespaces

我正在尝试编写一个宏,它可以帮助构建具有各种辅助函数的enum class,例如转换为字符串。在某种集合中提供对enum的所有值的访问是很自然的:

DEFINE_ENUM(Foo, Value1, Value2);

for (Foo v : enum_traits<Foo>::all_values) {
    // ...
}

似乎可以通过使DEFINE_ENUM()宏专门化一个公共enum_traits类来实现:

// globally:
template<typename T> struct enum_traits {};

// inside the macro:
#define DEFINE_ENUM(Name, ...) \
    /* define "enum class Name" ... */ \
    template<> struct my_enum_traits<Name> { \
        /* define all_values member */ \
    };

但是,如果DEFINE_ENUM(Foo, Value1, Value2);的扩展发生在命名空间内,那么它似乎不可能从该命名空间外部专门化模板:

template<typename T> struct enum_traits {};

namespace foo {
    // imagine DEFINE_ENUM is invoked here:

    enum class Foo { Value1, Value2 };

    // error: class template specialization of 'enum_traits'
    //   must occur at global scope
    template<> struct ::enum_traits<Foo> { /* ... */ };
}

有没有办法实现这一点,即宏可以“转义”包含其调用的命名空间,并从不同的命名空间(甚至是全局命名空间)中专门化一个模板?

1 个答案:

答案 0 :(得分:3)

好吧,我不能直接帮助这个特质模板专业化问题,我怀疑这是不可能的。

但是可以通过ADL

实现您的真正目标

见诀窍:

// globally:
template<typename T> 
using enum_traits = decltype(get_enum_traits(T{}));

诀窍是在get_enum_traits的命名空间中定义函数T。此函数应具有返回类型 - 应该是您的特征的类型。这个函数不需要实现 - 只有ADL方式从新定义的枚举类型的命名空间中获取类型。

// inside the macro:
#define DEFINE_ENUM(Name, ...) \
    /* define "enum class Name" ... */ \
    enum class Name { __VA_ARGS__ }; \
    struct Name##_type_traits { \  
       /* define all_values member */ \
    }; \  
    Name##_type_traits get_enum_traits(Name); 

有些demo确实有效。

#include <array>

// globally:
template<typename T> 
using enum_traits = decltype(get_enum_traits(T{}));

// inside the macro:
#define DEFINE_ENUM(Name, ...) \
    /* define "enum class Name" ... */ \
    enum class Name { __VA_ARGS__ }; \
    struct Name##_type_traits { \
         static constexpr std::array<Name,1> values{{ Name{} }}; \
    }; \
    Name##_type_traits get_enum_traits(Name); // does not need implementation


namespace foo {
    DEFINE_ENUM(Foo, Value1, Value2);
}

int main( ) {
    for (auto e: enum_traits<foo::Foo>::values)
    {}
}