Detecting Members
  ZXL8ALkrtBPG 2023年11月02日 68 0
cpp

在编译期判断类内是否存在某个成员或者成员函数,在模板编程中比较常见。

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

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  ZXL8ALkrtBPG   2023年11月02日   50   0   0 cpp
  PQYWJLBjS0G7   2023年11月02日   49   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   69   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   40   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   39   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   48   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   83   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   34   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   28   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   36   0   0 cpp
  ZXL8ALkrtBPG   2023年11月19日   18   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   58   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   70   0   0 Listcpp
  ZXL8ALkrtBPG   2023年11月02日   47   0   0 cpp
  ZXL8ALkrtBPG   2023年11月02日   68   0   0 cpp
  ZXL8ALkrtBPG   2023年11月19日   31   0   0 cpp
ZXL8ALkrtBPG