代码之家  ›  专栏  ›  技术社区  ›  ABu

指针指向成员的类的部分专门化

  •  0
  • ABu  · 技术社区  · 9 月前

    我试图创建一个函数对象,在给定一个指向成员的指针的情况下,它调用 std::hash 指向的成员(或调用函数成员的结果),但部分专门化有问题,无法编译:

    #include <iostream>
    #include <utility>
    
    template<auto MemberPtr>
    struct hash_member_t;
    
    template<class R, class C>
    struct hash_member_t<R C::*Member>
    {
        constexpr std::size_t operator()(C const& a) const noexcept
        { return std::hash<R>{}(a.*Member); }
    };
    
    template<class R, class C>
    struct hash_member_t<R (C::*Member)()>
    {
        constexpr std::size_t operator()(C const& a) const noexcept
        { return std::hash<R>{}(a.*Member()); }
    };
    
    template<auto Member>
    inline constexpr hash_member_t hash_member = hash_member_t<Member>{};
        
    struct A
    {
        int* a = nullptr;
    };
    
    int main(int, char**)
    { 
        A a;
        std::cout << hash_member<&A::a>(a) << '\n';
        return 0;
    }
    

    编译错误:

    main.cpp:8:34: error: template argument 1 is invalid
        8 | struct hash_member_t<R C::*Member>
          |                                  ^
    main.cpp:15:38: error: template argument 1 is invalid
       15 | struct hash_member_t<R (C::*Member)()>
          |                                      ^
    main.cpp: In instantiation of 'constexpr const hash_member_t<...auto...> hash_member<&A::a>':
    main.cpp:32:18:   required from here
    main.cpp:22:32: error: invalid use of incomplete type 'struct hash_member_t<&A::a>'
       22 | inline constexpr hash_member_t hash_member = hash_member_t<Member>{};
          |                                ^~~~~~~~~~~
    main.cpp:5:8: note: declaration of 'struct hash_member_t<&A::a>'
        5 | struct hash_member_t;
          |        ^~~~~~~~~~~~~
    

    上面是用g++编译的。使用clang++:

    main.cpp:8:28: error: type-id cannot have a name
    struct hash_member_t<R C::*Member>
                               ^~~~~~
    main.cpp:8:22: error: template argument for non-type template parameter must be an expression
    struct hash_member_t<R C::*Member>
                         ^~~
    main.cpp:4:15: note: template parameter is declared here
    template<auto MemberPtr>
                  ^
    main.cpp:15:28: error: expected unqualified-id
    struct hash_member_t<R (C::*Member)()>
                               ^
    main.cpp:15:29: error: use of undeclared identifier 'Member'
    struct hash_member_t<R (C::*Member)()>
                                ^
    main.cpp:22:46: error: implicit instantiation of undefined template 'hash_member_t<&A::a>'
    inline constexpr hash_member_t hash_member = hash_member_t<Member>{};
                                                 ^
    main.cpp:32:18: note: in instantiation of variable template specialization 'hash_member' requested here
        std::cout << hash_member<&A::a>(a) << '\n';
                     ^
    main.cpp:5:8: note: template is declared here
    struct hash_member_t;
           ^
    5 errors generated.
    

    语法或使用上下文有什么问题?

    1 回复  |  直到 9 月前
        1
  •  2
  •   463035818_is_not_an_ai    9 月前

    您混淆了非类型和类型模板参数。

    此模板

    template<auto MemberPtr> struct hash_member_t;
    

    有一个非类型参数。稍后,您尝试将其专门用于类型。我想有一个更好的方法来编写它,这就是我在修复你的代码后得到的:

    #include <iostream>
    #include <utility>
    
    template <typename T,T member> struct hash_member_impl;
    
    template<class R, class C,R C::*member>
    struct hash_member_impl<R C::*,member>
    {
        constexpr std::size_t operator()(C const& a) const noexcept
        { return std::hash<R>{}(a.*member); }
    };
    
    template<class R, class C,R (C::*member)()>
    struct hash_member_impl<R (C::*)(),member>
    {
        constexpr std::size_t operator()(C const& a) const noexcept
        { return std::hash<R>{}(a.*member()); }
    };
    
    
    template<auto MemberPtr>
    using hash_member = hash_member_impl<decltype(MemberPtr),MemberPtr>;
    
    struct A
    {
        int* a = nullptr;
    };
    
    int main(int, char**)
    { 
        A a;
        std::cout << hash_member<&A::a>{}(a) << '\n';
        return 0;
    }
    

    Live Demo

    重要的一点是

    template<auto MemberPtr>
    using hash_member = hash_member_impl<decltype(MemberPtr),MemberPtr>;
    

    你的参数是一个值,但你想专门研究它的类型,这就是你从 auto 非类型参数。

        2
  •  2
  •   ABu    9 月前

    正如评论和公认答案中指出的那样,问题在于模板参数是一个值,因此部分专门化需要一个实际值,比如指向成员的实际指针,但我打算检测接收到的指针的类型。

    对于有类似问题的人来说,想看看其他解决问题的方法,这是我最终使用的解决方案:

    #include <iostream>
    #include <utility>
    
    template<auto MemberPtr>
    struct hash_member_t
    {
        template<class T>
        constexpr std::size_t operator()(T const& a) const noexcept
        { return _call(a, MemberPtr); }
    
    private:
        template<class R, class C>
        constexpr std::size_t _call(C const& a, R C::*) const noexcept
        { return std::hash<R>{}(a.*MemberPtr); }
        
        template<class R, class C>
        constexpr std::size_t _call(C const& a, R (C::*)() const) const noexcept
        { return std::hash<R>{}((a.*MemberPtr)()); }
        
        template<class R, class C>
        constexpr std::size_t _call(C const& a, R (C::*)() const noexcept) const noexcept
        { return std::hash<R>{}((a.*MemberPtr)()); }
    };
    
    template<auto Member>
    inline constexpr hash_member_t hash_member = hash_member_t<Member>{};
        
    struct A
    {
        int a = 8;
        std::string f() const noexcept { return "hi"; }
    };
    
    int main(int, char**)
    { 
        A a;
        std::cout << hash_member<&A::a>(a) << ' ' << hash_member<&A::f>(a) << '\n';
        return 0;
    }
    

    这打印了“8 11290347552884584064”。