Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Partial specialization of a class for pointer to members

I’m trying to create a function object where, given a pointer to a member, it calls std::hash of the pointed-to member (or the result of calling the function member), but there’s something wrong about the partial specializations and it doesn’t compile:

#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;
}

Compilation error:

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;
      |        ^~~~~~~~~~~~~

The above is compiled with g++. With clang++:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

What’s wrong with the syntax or the usage context?

My solution

As pointed out in the comments and the accepted answer, the problem is that the template parameter is a value, and so the partial specialization requires an actual value, like an actual pointer-to-member, but I intented to detect the type of the received pointer.

I finally made it by overloading instead of using auxiliary classes:

#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;
}

This prints "8 11290347552884584064".

>Solution :

You are confusing non-type and type template arguments.

This template

template<auto MemberPtr> struct hash_member_t;

Has a non-type argument. Later you try to specialize it for types. I suppose there is a nicer way to write it, this is just what I got after fixing your code:

#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

The important line is this

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

Your argument is a value, but you want to specialize on its type and this is how you get both the type and value from the auto non-type argument.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading