用于检测 类 中的非静态非类型成员的宏
Macro for detecting non-static non-type members in classes
在 C++ 模板 - 完整指南,第 2 版,第 434 页,定义了一个宏来生成谓词来测试 [=40= 中非类型成员的存在]:
#include <type_traits>
#define DEFINE_HAS_MEMBER(Member) \
template<typename T, typename = void> \
struct HasMemberT_##Member : std::false_type {}; \
template<typename T> \
struct HasMemberT_##Member<T, std::void_t<decltype(&T::Member)>> : std::true_type {};
正文是
It would not be difficult to modify the partial specialization to exclude cases wehre &T::Member
is not a pointer-to-member type (which amounts to excluding static
data members).
所以我研究了 static_assert
s 和编译器错误,并记住了 classes 成员的 static
和非 static
类型:
struct A {
int begin;
static int end;
};
static_assert(std::is_same<decltype(&A::begin), decltype(A::begin) A::*>::value, ""); // passes
static_assert(std::is_same<decltype(&A::end), decltype(A::end)*>::value, ""); // passes
并且认为也许对上述 lambda 的一个很好的修改旨在检测非 static
成员只是改变
: std::true_type
至
: std::is_same<decltype(&T::Member), decltype(T::Member) T::*>
而如果我只想检查 static
成员,我可以将其更改为
: std::is_same<decltype(&T::Member), decltype(T::Member)*>
真的这么简单吗?还是我忽略了一些重要的事情?
您忽略了非静态成员 函数。
DEFINE_HAS_MEMBER
创建的特征的主要用例是检查特定方法是否存在。借用问题中的术语,范围由 begin()
和 end()
定义。虽然原始宏创建的特征有效,但您的额外条件 does not.
prog.cc:7:61: error: invalid use of non-static member function 'int A::begin()'
7 | static_assert(std::is_same<decltype(&A::begin), decltype(A::begin) A::*>::value, "");
| ^~~~~
prog.cc:7:72: error: template argument 2 is invalid
7 | static_assert(std::is_same<decltype(&A::begin), decltype(A::begin) A::*>::value, "");
|
标准语中的原因是这样的:
[expr.prim.id]
2 An id-expression that denotes a non-static data member or non-static member function of a class can only be used:
- as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or
- to form a pointer to member ([expr.unary.op]), or
- if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [ Example:
struct S {
int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK
— end example ]
这些是只有 对T::Member
之类的有效用法。非静态成员函数没有击中任何子弹,因此当用于定义 HasMemberT_##Member
的特化主体时,该构造是格式错误的。没有 SFINAE,只是一个硬错误。
在 C++ 模板 - 完整指南,第 2 版,第 434 页,定义了一个宏来生成谓词来测试 [=40= 中非类型成员的存在]:
#include <type_traits>
#define DEFINE_HAS_MEMBER(Member) \
template<typename T, typename = void> \
struct HasMemberT_##Member : std::false_type {}; \
template<typename T> \
struct HasMemberT_##Member<T, std::void_t<decltype(&T::Member)>> : std::true_type {};
正文是
It would not be difficult to modify the partial specialization to exclude cases wehre
&T::Member
is not a pointer-to-member type (which amounts to excludingstatic
data members).
所以我研究了 static_assert
s 和编译器错误,并记住了 classes 成员的 static
和非 static
类型:
struct A {
int begin;
static int end;
};
static_assert(std::is_same<decltype(&A::begin), decltype(A::begin) A::*>::value, ""); // passes
static_assert(std::is_same<decltype(&A::end), decltype(A::end)*>::value, ""); // passes
并且认为也许对上述 lambda 的一个很好的修改旨在检测非 static
成员只是改变
: std::true_type
至
: std::is_same<decltype(&T::Member), decltype(T::Member) T::*>
而如果我只想检查 static
成员,我可以将其更改为
: std::is_same<decltype(&T::Member), decltype(T::Member)*>
真的这么简单吗?还是我忽略了一些重要的事情?
您忽略了非静态成员 函数。
DEFINE_HAS_MEMBER
创建的特征的主要用例是检查特定方法是否存在。借用问题中的术语,范围由 begin()
和 end()
定义。虽然原始宏创建的特征有效,但您的额外条件 does not.
prog.cc:7:61: error: invalid use of non-static member function 'int A::begin()'
7 | static_assert(std::is_same<decltype(&A::begin), decltype(A::begin) A::*>::value, "");
| ^~~~~
prog.cc:7:72: error: template argument 2 is invalid
7 | static_assert(std::is_same<decltype(&A::begin), decltype(A::begin) A::*>::value, "");
|
标准语中的原因是这样的:
[expr.prim.id]
2 An id-expression that denotes a non-static data member or non-static member function of a class can only be used:
- as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or
- to form a pointer to member ([expr.unary.op]), or
- if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [ Example:
— end example ]struct S { int m; }; int i = sizeof(S::m); // OK int j = sizeof(S::m + 42); // OK
这些是只有 对T::Member
之类的有效用法。非静态成员函数没有击中任何子弹,因此当用于定义 HasMemberT_##Member
的特化主体时,该构造是格式错误的。没有 SFINAE,只是一个硬错误。