在编译期判断类内是否存在某个成员或者成员函数,在模板编程中比较常见。
Detecting Nontype Member
namespace NontypeMember {
#define HAS_MEMBER(Member) \
template<typename T, typename = std::void_t<>> \
struct Has##Member : std::false_type { \
}; \
template<typename T> \
struct Has##Member<T, std::void_t<decltype(T::Member)>> : std::true_type { \
};
#define CHECK_MEMBER(T, Member) \
Has##Member<T>::value
HAS_MEMBER(id)
HAS_MEMBER(name)
struct MyTest {
int id;
};
}
TEST(testCase, NontypeMember)
{
using namespace NontypeMember;
static_assert(CHECK_MEMBER(MyTest, id), "hase no m_member");
static_assert(!CHECK_MEMBER(MyTest, name), "hase no m_member");
}
利用SFINE进行判断,在c++20中可以通过concepts来实现。
Detecting Funtion Member
namespace FunctionMember {
#define HAS_FUNCTION(Function, ...) \
template<typename T, typename = std::void_t<>> \
struct Has##Function : std::false_type { \
}; \
template<typename T> \
struct Has##Function<T, std::void_t<decltype(std::declval<T>().Function())>> : std::true_type { \
};
#define CHECK_FUNCTION(T, Function) \
Has##Function<T>::value
HAS_FUNCTION(Init)
HAS_FUNCTION(DeInit)
struct MyTest {
int id;
void Init() {}
};
}
TEST(testCase, FunctionMember)
{
using namespace FunctionMember;
static_assert(CHECK_FUNCTION(MyTest, Init), "hase no m_member");
static_assert(!CHECK_FUNCTION(MyTest, DeInit), "hase no m_member");
}
How to detect private member?
通过上面的常用手法可以满足常见的应用场景,但是可以发现上面检测的member均为public成员,那么进一步想检测是否存在某个私有成员变量或者成员函数应该如何操作?我们基于上面的例子进行改造:
struct MyTest {
private:
int id;
void Init() {}
};
则发现出现了误报:
static_assert(CHECK_FUNCTION(MyTest, Init), "hase no m_member"); // 无法检测到Init成员
这时由于Init成员函数为私有,因此HasInit无法匹配到特化版本,匹配到泛化版本因此为false;
可以尝试使用:decltype(std::declval<MyTest>().Init()) ,则会提示报错:
MyTest::Init() is private within this context
那如何解决?在cppreference中有这么一句神奇的说明:
Explicit instantiation definitions ignore member access specifiers: parameter types and return types may be private.
即显式实例化定义忽略成员访问说明符:参数类型和返回类型可能是私有的,改造代码为:
namespace FunctionMember {
#define HAS_FUNCTION(Function, ...) \
template<typename T, typename = std::void_t<>> \
struct Has##Function : std::false_type { \
}; \
template<typename T> \
struct Has##Function<T, std::void_t<decltype(std::declval<T>().Function())>> : std::true_type { \
}; \
template <auto hasResult> \
struct HasImpl##Function { \
constexpr static auto value = hasResult; \
};
#define HAS_FUNCTION_INS(T, Function) \
template struct HasImpl##Function<Has##Function<T>::value>; // 关键点
#define CHECK_FUNCTION(T, Function) \
HasImpl##Function<Has##Function<T>::value>::value
HAS_FUNCTION(Init)
HAS_FUNCTION(DeInit)
struct MyTest {
private:
int id;
void Init() {}
};
HAS_FUNCTION_INS(MyTest, Init);
HAS_FUNCTION_INS(MyTest, DeInit);
}
TEST(testCase, FunctionMember)
{
using namespace FunctionMember;
static_assert(CHECK_FUNCTION(MyTest, Init), "hase no m_member"); // 编译期检测私有成员函数是否存在
static_assert(!CHECK_FUNCTION(MyTest, DeInit), "hase no m_member");
}
通过显示实例化触发成员函数或者成员变量的检测,并存储检测结果并在编译期判断。
参考资料
【1】https://en.cppreference.com/w/cpp/language/class_template
标签:FUNCTION,Function,struct,MyTest,##,Init,Members,Detecting From: https://blog.51cto.com/u_13137973/7424429