是否可以检测 C++ 中的命名空间成员资格?
对于 C++ 类型,<type_traits>
标头为我们提供了许多有用的编译时反射功能.例如.std::is_base_of<B, D>::value
在编译时确定 B
是否是 D
的基类.
For C++ types, the <type_traits>
header gives us many useful compile-time reflection capabilities. E.g. std::is_base_of<B, D>::value
determines at compile-time whether B
is a base class of D
.
我想知道是否可以按照类似的方式检测命名空间成员资格?例如.给定一个类型为 T
的命名空间 N
,有没有办法确定 T
是否包含在 N
中IS_NAMESPACE_MEMBER_OF(T,N)
形式的宏表达式.
I wonder if it would be possible to detect namespace membership along similar lines? E.g. given a namespace N
with a type T
, is there a way to determine whether T
is contained within N
using a macro expression of the form IS_NAMESPACE_MEMBER_OF(T,N)
.
我更喜欢通过任何类型的 SFINAE/ADL 类型的技巧来获得编译时答案.或者,如果不可能,某种推理为什么标准不允许这样做.
I'd prefer a compile-time answer through any sort of SFINAE / ADL type of trick. Or, if it isn't possible, some sort of reasoning why the Standard would not allow this.
一个不可移植的运行时破解方法是为 N
正则表达式 typeid(T).name()
,但这相当乏味且不适合编译-时间.
A non-portable and run-time hack would be to regex typeid(T).name()
for N
, but this is rather tedious and not at compile-time.
EDIT1:正如 K-ballo 所指出的,命名空间不能用作模板参数,因此类型特征似乎是不可能的.
EDIT1: as pointed out by K-ballo, a namespace cannot be used as a template parameter so a type-trait seems impossible.
EDIT2:这是 K-ballo 暗示的骨架:在那里可以(或不能?)进行哪些漂亮的测试?
EDIT2: here's the skeleton as hinted to by K-ballo: what nifty test can (or cannot?) be cooked up there?
#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!
推荐答案
您可以测试 namespace
是否可以通过 ADL 从类型中访问(由编译器查找).
You can test whether the namespace
is accessible (looked up by the compiler), via ADL, from the type.
假设我们要检查类型 A
是否来自 namespace foo
,我们可以尝试使用只出现在 foo
中的类型(例如通用函数 foo::foo_inner_func(T&&)
)通过使用 A
来查看我们是否到达命名空间.如果我们在 SFINAE 上下文中执行此操作,则可以得到我们正在寻找的答案:namespace foo
是否可以通过 A
访问.
Suppose that we want to check if type A
comes from namespace foo
, we can try to use a type that appears only in foo
(e.g. a generic function foo::foo_inner_func(T&&)
) via the use of A
to see if we reach the namespace. If we do it in a SFINAE context then this can result with the answer we are looking for: whether namespace foo
is accessible via A
.
在许多情况下,这将是类型是否属于此命名空间的答案,但在某些情况下,它可能会识别 ADL 可访问的命名空间,即使该类型并非来自此命名空间.例如,如果 A
来自 namespace foo
并且从 A
派生的 B
来自另一个命名空间,B
仍然看见";foo
通过 ADL.还有 std::vector<A>
看见"foo
通过 ADL(也看到"std
通过 ADL).
In many cases that would be the answer of whether the type belongs to this namespace, but in some cases it may identify a namespace as accessible by ADL even though the type doesn't come from this namespace. For example if A
is from namespace foo
and B
which derives from A
is from another namespace, B
still "sees" foo
via ADL. Also std::vector<A>
"sees" foo
via ADL (and also "sees" std
via ADL).
这里已经介绍了使用 ADL 的想法:检查如果一个类型来自特定的命名空间.
The idea of using ADL was already presented here: Check if a type is from a particular namespace.
这是允许查询任何类型(几乎)任何命名空间(几乎)的宏版本:
Here is the macro version that allows querying any type (almost) for any namespace (almost):
#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 上下文中测试:
Testing in findme context:
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
}
}
在 main 中测试:
Testing in main:
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
相关文章