Search code examples
c++c++17c++20stddata-member-pointers

Data member pointers as associative container keys


I am trying to create an std::set of pointers to data members. However, I can't find a method to sort or hash such pointers.

They can't be compared with operator<, they don't seem to be supported by std::less and there is no standard integer type that is guaranteed to hold their representation (they might not fit in std::uintptr_t).

This is what I tried first (https://godbolt.org/z/K8ajn3rM8) :

#include <set>

struct foo
{
    int x;
    int y;
};

using t_member_ptr = int (foo::*);

const std::set<t_member_ptr> members = {
    &foo::x,
    &foo::y
};

It produces the error error: invalid operands of types 'int foo::* const' and 'int foo::* const' to binary 'operator<'. The full error message also implies this occurs during instantiation of std::less.

I found a similar question (Set of pointer to member) but it dates back to C++14 and the answers boil down to "put your pointers in a vector and perform a linear search instead".

Has there been any changes with C++17 or C++20 that make it possible to use pointers to data members as keys for standard associative containers?


Solution

  • Compare them bytewise, e.g. using this comparator:

    #include <cstring>
    #include <type_traits>
    
    struct BitLess
    {
        template <typename T>
        requires std::has_unique_object_representations_v<T>
        constexpr bool operator()(const T &a, const T &b) const
        {
            return std::memcmp(reinterpret_cast<const char *>(&a), reinterpret_cast<const char *>(&b), sizeof(T)) < 0;
        }
    };
    

    Checking std::has_unique_object_representations_v<T> ensures that there's no padding inside. It tried it on GCC, Clang, and MSVC, and it returned true for member pointers on all three.