如何在 C++ 中分解指向成员的指针(获取 class 和成员类型)?
How to decompose a pointer-to-member in C++ (get class and member types)?
我有这个场景:
#include <iostream>
class SomeClass
{
public:
int _int;
};
#define DO_SOME_STUFF(ptr) std::cout << /* Print the typeid().hash_code() of the type which ptr is poiting to (int) */;
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
DO_SOME_STUFF(ptr_to_int_member)
}
我想知道 ptr
指向的是哪种类型(目前是 int
)。
知道 class 拥有 int
也很有用(目前是 SomeClass
)。
你可以用一个“template
技巧”来做到这一点:
template<typename T>
struct PointerToMemberDecomposer {};
template<typename T, typename P>
struct PointerToMemberDecomposer<P T::*>
{
using ClassType = T;
using MemberType = P;
};
并将您的代码更改为:
#include <iostream>
template<typename T>
struct PointerToMemberDecomposer {};
template<typename T, typename P>
struct PointerToMemberDecomposer<P T::*>
{
using ClassType = T;
using MemberType = P;
};
class SomeClass
{
public:
int _int;
};
#define DO_SOME_STUFF(ptr) std::cout << typeid(PointerToMemberDecomposer<decltype(ptr)>::MemberType).hash_code();
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
DO_SOME_STUFF(ptr_to_int_member)
}
定义几个模板化别名可以使代码更简洁:
#define GET_POINTER_TO_MEMBER_CLASS_TYPE(ptr) PointerToMemberDecomposer<decltype(ptr)>::ClassType
#define GET_POINTER_TO_MEMBER_MEMBER_TYPE(ptr) PointerToMemberDecomposer<decltype(ptr)>::MemberType
因此您可以将 DO_SOME_STUFF
更改为:
#define DO_SOME_STUFF(ptr) std::cout << typeid(GET_POINTER_TO_MEMBER_MEMBER_TYPE(ptr)).hash_code();
说明
这种技术叫做Partial template specialization
。
当 pointer-to-member
类型作为模板参数传递时,将使用 PointerToMemberDecomposer
的第二个定义;并且会捕获新的 T
和 P
typename
s。使用那些新的 typename
;它将定义两个类型别名(ClassType
和 MemberType
),因此 T
和 P
可以在 PointerToMemberDecomposer
结构之外使用。
使用PointerToMemberDecomposer
时;您应该使用 decltype 运算符,其作用类似于 Python 中的 type
或 C# 中的 typeof
。 decltype(x)
传递 x
的类型而不是 x
本身。
更新
如463035818_is_not_a_number
所述;宏可以替换为 templated aliases
template <typename T>
using ClassTypeFromPtrToMember_t = typename PointerToMemberDecomposer<T>::ClassType;
template <typename T>
using MemberTypeFromPtrToMember_t = typename PointerToMemberDecomposer<T>::MemberType;
但是你仍然应该使用 decltype
而 DO_SOME_STUFF
是一个宏而不是模板函数,我们不能直接访问 ptr
的类型(参见 DO_SOME_STUFF
的模板函数版本):
#define DO_SOME_STUFF(ptr) std::cout << typeid(MemberTypeFromPtrToMember_t<decltype(ptr)>).hash_code();
在这种情况下; DO_SOME_STUFF
可以转换为模板函数。但是你可能想要用宏参数填充一个非捕获的 lambda;这要求 DO_SOME_STUFF
是一个宏。
此外,您可能希望将 ClassType
和 MemberType
更改为 type
并创建两个单独的 struct
s(或 class
es)用于检索那些类型别名;如果你想让 PointerToMemberDecomposer
看起来像 C++ 的标准库。
了解更多详情;参见
只是总结对一些其他方面很好的答案的评论...
成员别名通常只命名为 type
。最好避免使用宏 (Why are preprocessor macros evil and what are the alternatives?),为了减少调用者的冗长,您可以使用函数模板:
#include <iostream>
#include <typeinfo>
template<typename T>
struct TypeFromPtrToMember; // needs no definition
template<typename T, typename P>
struct TypeFromPtrToMember<P T::*>
{
using type = T;
};
class SomeClass
{
public:
int _int;
};
template <typename T>
void do_some_stuff(T t){
std::cout << typeid(typename TypeFromPtrToMember<T>::type).hash_code();;
}
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
do_some_stuff(ptr_to_int_member);
}
将成员别名命名为 type
是 so common 即使您需要两个特征,我也会这样做。另一个特征与 using type = P;
.
基本相同
在上面,使用 trait 时仍然需要写 typename
的小烦恼(因为 TypeFromPtrToMember<T>::type
是一个 dependent name). Since C++11 we can use a template alias 来帮助解决这个问题。模板别名不能是部分专业化的,但我们已经有了特质,只需要转发到那个:
template <typename T>
using TypeFromPtrToMember_t = typename TypeFromPtrToMember<T>::type;
这样 do_some_stuff
可以是:
template <typename T>
void do_some_stuff(T t){
std::cout << typeid(TypeFromPtrToMember_t<T>).hash_code();;
}
我希望你同意现在不再需要宏了。
我有这个场景:
#include <iostream>
class SomeClass
{
public:
int _int;
};
#define DO_SOME_STUFF(ptr) std::cout << /* Print the typeid().hash_code() of the type which ptr is poiting to (int) */;
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
DO_SOME_STUFF(ptr_to_int_member)
}
我想知道 ptr
指向的是哪种类型(目前是 int
)。
知道 class 拥有 int
也很有用(目前是 SomeClass
)。
你可以用一个“template
技巧”来做到这一点:
template<typename T>
struct PointerToMemberDecomposer {};
template<typename T, typename P>
struct PointerToMemberDecomposer<P T::*>
{
using ClassType = T;
using MemberType = P;
};
并将您的代码更改为:
#include <iostream>
template<typename T>
struct PointerToMemberDecomposer {};
template<typename T, typename P>
struct PointerToMemberDecomposer<P T::*>
{
using ClassType = T;
using MemberType = P;
};
class SomeClass
{
public:
int _int;
};
#define DO_SOME_STUFF(ptr) std::cout << typeid(PointerToMemberDecomposer<decltype(ptr)>::MemberType).hash_code();
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
DO_SOME_STUFF(ptr_to_int_member)
}
定义几个模板化别名可以使代码更简洁:
#define GET_POINTER_TO_MEMBER_CLASS_TYPE(ptr) PointerToMemberDecomposer<decltype(ptr)>::ClassType
#define GET_POINTER_TO_MEMBER_MEMBER_TYPE(ptr) PointerToMemberDecomposer<decltype(ptr)>::MemberType
因此您可以将 DO_SOME_STUFF
更改为:
#define DO_SOME_STUFF(ptr) std::cout << typeid(GET_POINTER_TO_MEMBER_MEMBER_TYPE(ptr)).hash_code();
说明
这种技术叫做Partial template specialization
。
当 pointer-to-member
类型作为模板参数传递时,将使用 PointerToMemberDecomposer
的第二个定义;并且会捕获新的 T
和 P
typename
s。使用那些新的 typename
;它将定义两个类型别名(ClassType
和 MemberType
),因此 T
和 P
可以在 PointerToMemberDecomposer
结构之外使用。
使用PointerToMemberDecomposer
时;您应该使用 decltype 运算符,其作用类似于 Python 中的 type
或 C# 中的 typeof
。 decltype(x)
传递 x
的类型而不是 x
本身。
更新
如463035818_is_not_a_number
所述;宏可以替换为 templated aliases
template <typename T>
using ClassTypeFromPtrToMember_t = typename PointerToMemberDecomposer<T>::ClassType;
template <typename T>
using MemberTypeFromPtrToMember_t = typename PointerToMemberDecomposer<T>::MemberType;
但是你仍然应该使用 decltype
而 DO_SOME_STUFF
是一个宏而不是模板函数,我们不能直接访问 ptr
的类型(参见 DO_SOME_STUFF
的模板函数版本):
#define DO_SOME_STUFF(ptr) std::cout << typeid(MemberTypeFromPtrToMember_t<decltype(ptr)>).hash_code();
在这种情况下; DO_SOME_STUFF
可以转换为模板函数。但是你可能想要用宏参数填充一个非捕获的 lambda;这要求 DO_SOME_STUFF
是一个宏。
此外,您可能希望将 ClassType
和 MemberType
更改为 type
并创建两个单独的 struct
s(或 class
es)用于检索那些类型别名;如果你想让 PointerToMemberDecomposer
看起来像 C++ 的标准库。
了解更多详情;参见
只是总结对一些其他方面很好的答案的评论...
成员别名通常只命名为 type
。最好避免使用宏 (Why are preprocessor macros evil and what are the alternatives?),为了减少调用者的冗长,您可以使用函数模板:
#include <iostream>
#include <typeinfo>
template<typename T>
struct TypeFromPtrToMember; // needs no definition
template<typename T, typename P>
struct TypeFromPtrToMember<P T::*>
{
using type = T;
};
class SomeClass
{
public:
int _int;
};
template <typename T>
void do_some_stuff(T t){
std::cout << typeid(typename TypeFromPtrToMember<T>::type).hash_code();;
}
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
do_some_stuff(ptr_to_int_member);
}
将成员别名命名为 type
是 so common 即使您需要两个特征,我也会这样做。另一个特征与 using type = P;
.
在上面,使用 trait 时仍然需要写 typename
的小烦恼(因为 TypeFromPtrToMember<T>::type
是一个 dependent name). Since C++11 we can use a template alias 来帮助解决这个问题。模板别名不能是部分专业化的,但我们已经有了特质,只需要转发到那个:
template <typename T>
using TypeFromPtrToMember_t = typename TypeFromPtrToMember<T>::type;
这样 do_some_stuff
可以是:
template <typename T>
void do_some_stuff(T t){
std::cout << typeid(TypeFromPtrToMember_t<T>).hash_code();;
}
我希望你同意现在不再需要宏了。