【问题标题】:How to decompose a pointer-to-member in C++ (get class and member types)?如何在 C++ 中分解指向成员的指针(获取类和成员类型)?
【发布时间】:2021-07-05 08:03:15
【问题描述】:

我有这种情况:

#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)。 知道哪个类拥有 int 也很有用(目前是 SomeClass)。

【问题讨论】:

    标签: c++ pointers pointer-to-member


    【解决方案1】:

    你可以用“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 specializationPointerToMemberDecomposer 的第二个定义将在 pointer-to-member 类型作为模板参数传递时使用;并将捕获新的TP typenames。使用那些新的typenames;它将定义两个类型别名(ClassTypeMemberType),因此 TP 可以在 PointerToMemberDecomposer 结构之外使用。

    使用PointerToMemberDecomposer时;您应该使用decltype 运算符,其作用类似于Python 中的type 或C# 中的typeofdecltype(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 的类型(请参阅463035818_is_not_a_number's answer 以获取DO_SOME_STUFF 的模板函数版本):

    #define DO_SOME_STUFF(ptr) std::cout << typeid(MemberTypeFromPtrToMember_t<decltype(ptr)>).hash_code();
    

    在这种情况下; DO_SOME_STUFF 可以转换为模板函数。但是您可能希望例如用宏参数填充非捕获 lambda;这要求 DO_SOME_STUFF 是一个宏。

    此外,您可能希望将ClassTypeMemberType 更改为type 并创建两个单独的structs(或classes)来检索这些类型别名;如果你想让PointerToMemberDecomposer 看起来像 C++ 的标准库。

    更多详情;见463035818_is_not_a_number's answer

    【讨论】:

    • “使之更干净的宏” 之前,答案非常好。不。如果你想让它更干净,你可以使用类型别名。
    • fwiw,通常将 memebr 别名命名为 type。你可以有两个独立的特征,Member&lt;decltype(ptr)&gt;::typeClass&lt;decltype(ptr)&gt;::type
    • 同意光谱,很好的答案,只有宏有点偏离。为了便于阅读,您可以using type = PointerToMemberDecomposer&lt;decltype(ptr)&gt;::Class。您的宏的输入量并不比原来的少很多,并且具有作为宏的缺点 (stackoverflow.com/questions/14041453/…)。
    • minotr nitpick:“它声明了表达式的类型..”不太正确。而是"Inspects the declared type of an entity or the type and value category of an expression. "
    • 你确实可以用它制作一个函数模板。 template&lt;A&gt; void DO_SOME_STUFF(A&amp; a) { std::cout &lt;&lt; /* Something with PointerToMemberDecomposer&lt;A&gt; */} - 甚至不需要使用decltype
    【解决方案2】:

    只是将 cmets 总结为一些其他很好的答案......

    成员别名通常命名为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);
    }
    

    将成员别名命名为typeso common,即使您需要两个特征,我也会这样做。另一个特点与using type = P;基本相同。

    在上面,使用 trait 时仍然需要写 typename 的小烦恼(因为 TypeFromPtrToMember&lt;T&gt;::typedependent name)。从 C++11 开始,我们可以使用 template alias 来帮助解决这个问题。模板别名不能部分特化,但我们已经具备了 trait,只需要转发即可:

    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();;
    }
    

    我希望您同意现在不再需要宏。

    【讨论】:

      猜你喜欢
      • 2021-12-22
      • 2018-03-24
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多