在C++中检测命名空间成员是否存在,是否可行?

4
对于C ++类型,<type_traits>头文件为我们提供了许多有用的编译时反射功能。例如:std::is_base_of<B,D>::value可以在编译时确定B是否是D的基类。
我想知道是否可能通过类似的方式检测命名空间成员资格?例如:给定一个具有类型T的命名空间N,是否有一种方法可以使用形式为IS_NAMESPACE_MEMBER_OF(T,N)的宏表达式来确定T是否包含在N中。
我更喜欢通过任何类型的SFINAE / ADL技巧获得编译时答案。或者,如果不可能,一些说明为什么标准不允许这样做。
对于非便携式和运行时黑客,可以对typeid(T).name()进行regex以获取N,但这相当繁琐且不在编译时。 EDIT1 :如K-ballo指出的那样,不能将命名空间用作模板参数,因此似乎不可能使用类型特征。 EDIT2 :这是由K-ballo提示的骨架:那里可以制作出什么漂亮的测试?
#define IS_NAMESPACE_MEMBER_OF(T, N) \
                                     \
// global declaration                \
void test(T);                        \
                                     \
// namespace declaration             \
namespace N {                        \
    void test(T);                    \
}                                    \
                                     \
// some clever name lookup / sizeof / SFINAE test!     

1
假设你有以下代码:struct T {}; namespace N { struct T; },你希望结果是true还是false?那么对于这段代码namespace A { struct T {}; } namespace B { using ::A::T; }T是否属于A命名空间? - David Rodríguez - dribeas
1
在编辑中使用宏方法(即创建函数)的问题在于,它的使用范围非常有限,因为它只能在命名空间级别和任何其他命名空间之外使用... - David Rodríguez - dribeas
@DavidRodríguez-dribeas 关于您的第一条评论,我想说:是的,TA的一部分,也是B的一部分。为什么T不能成为多个(非嵌套)命名空间的成员呢?毕竟,T还可以是许多其他类的基类或派生类。我不是要确定T的作用域链的整个链。只是一个简单的问题:T是否包含在N中(通过直接定义或通过using语句)。 - TemplateRex
@DavidRodríguez-dribeas 您的第二个评论是一个好观点。但是,我们可以将整个宏包装在另一个命名空间中,以防止污染全局范围,对吗? - TemplateRex
根据定义,您无法在命名空间中包装宏。宏扩展发生在编译器处理命名空间之前。至于问题,这很重要,因为在语言中,类型仅在一个命名空间中定义,即使您可以将其引入其他命名空间、类型或函数的查找目的。我真的不知道这有什么用处,但这些是需要考虑的一些事情。 - David Rodríguez - dribeas
2个回答

4

命名空间不是有效的模板参数,因此它永远不能成为类特征。也许您可以通过宏做一些晦涩的事情。您可以在测试命名空间中注入函数,并与 sizeof/decltype 技巧一起使用 ADL 来查看选择哪个重载。


感谢您指出我犯的愚蠢错误,即让命名空间成为模板参数:已在问题中进行了修复。 - TemplateRex
你注入名称的建议听起来很有趣。这有点类似于将函数参数注入到一个sizeof +测试函数中用于类型特性。你有任何具体的代码建议让我尝试吗? - TemplateRex
3
确实,这很相似。然而,ADL只会告诉你命名空间是否是类关联命名空间集的一部分,而不会告诉你该类是否属于这样的命名空间。我相信可以使用有关命名空间的某些复杂属性来测试命名空间成员身份,但我认为这并不实际值得深思熟虑。 - K-ballo
再次感谢您提供有关关联命名空间集的进一步提示。无论如何,正如标签“language-lawyer”所示,我确实发现它很有用!如果您对如何结合宏、名称查找和大小属性的微妙特性有更多想法,我会非常感兴趣(考虑赏金)。 - TemplateRex
前一段时间我发现了命名空间的一个有趣属性,我接近找到了您问题的解决方案。不幸的是,我无法记起它基于什么,但它并不是ADL。这让我相信这是可能的,我已经接近一个有效的解决方案了。我知道,这并不是非常有帮助... - K-ballo

1
您可以通过使用ADL(关联名称查找)从类型中测试namespace是否可访问(由编译器查找)。假设我们想要检查类型A是否来自namespace foo,我们可以尝试使用仅出现在foo中的类型(例如泛型函数foo::foo_inner_func(T&&)),通过使用A来查看是否到达了该命名空间。如果我们将其放在SFINAE上下文中,则可以得出我们正在寻找的答案:通过A是否可以访问namespace foo
在许多情况下,这将是判断类型是否属于此命名空间的答案,但在某些情况下,它可能会标识一个命名空间作为ADL可访问的,即使该类型不来自此命名空间。例如,如果A来自namespace foo,而从A派生的B来自另一个命名空间,则B仍然通过ADL“看到”foo。此外,std::vector<A>也通过ADL“看到”foo(并且还通过ADL“看到”std)。
使用ADL的想法已经在这里提出:检查类型是否来自特定命名空间
以下是允许查询任何类型(几乎)的任何命名空间(几乎)的宏版本:
#define create_ns_checker(ns) \
namespace ns { \
    template <typename T> \
    constexpr std::true_type ns##FindmeNsADLHelper(T&&); \
} \
namespace ns##_type_traits { \
    class ns##SecondBestMatchType {}; \
    class ns##BestExactMatchType : public ns##SecondBestMatchType {}; \
    namespace helpers { \
        template <typename T> \
        auto TestNs(ns##_type_traits::ns##BestExactMatchType) \
               -> decltype(ns##FindmeNsADLHelper(std::declval<T>())); \
        template <typename T> \
        auto TestNs(ns##_type_traits::ns##SecondBestMatchType) \
               -> std::false_type; \
    } \
    template <typename T> \
    constexpr bool ns##IsFindmeNs() { \
        return decltype(helpers::TestNs<std::decay_t<T>> \
                           (ns##BestExactMatchType{}))::value; \
    } \
}

#define is_in_ns(Type, ns) \
(ns##_type_traits::ns##IsFindmeNs<Type>())

一个小型的打印工具:

#define print_is_in_ns(Type, ns) \
[]() { \
    std::cout << #Type << " in " << #ns << ": "  \
              << is_in_ns(Type, ns) << std::endl; \
}()

用宏创建棋子:
create_ns_checker(findme)
create_ns_checker(other)
create_ns_checker(std)

检查以下类型:
namespace other {
    struct B {};
}

struct C {};

namespace findme {
    struct A {};

    namespace inner {
        struct A {};
    }
    create_ns_checker(inner)
}

在findme的上下文中测试:
namespace findme {
    void test() {
        using namespace other;
        // add the below in and the results change, as it should!
          // using inner::A;
        using std::string;
        std::cout << std::boolalpha;
        print_is_in_ns(int, std);          // false
        print_is_in_ns(string, std);       // true
        print_is_in_ns(A, findme);         // true
        print_is_in_ns(A, inner);          // false
        print_is_in_ns(inner::A, findme);  // false
        print_is_in_ns(inner::A, inner);   // true
        print_is_in_ns(B, findme);         // false
        print_is_in_ns(B, other);          // true
        print_is_in_ns(C, findme);         // false
    }
}

在主函数中进行测试:

int main() {
    using std::string;
    using findme::A;
    std::cout << std::boolalpha;
    print_is_in_ns(int, std);                 // false
    print_is_in_ns(string, std);              // true
    print_is_in_ns(string, findme);           // false
    print_is_in_ns(findme::A, findme);        // true
    print_is_in_ns(findme::inner::A, findme); // false
    print_is_in_ns(other::B, findme);         // false
    print_is_in_ns(other::B, other);          // true
    print_is_in_ns(C, findme);                // false
    print_is_in_ns(std::vector<A>, findme); // falsely says true :-(
    print_is_in_ns(std::vector<A>, std);      // true
    std::cout << "-----------------" << std::endl;
    findme::test();
}

代码:https://godbolt.org/z/8Ed89v


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