6

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?

François Andrieux
  • 28,148
  • 6
  • 56
  • 87

1 Answers1

7

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.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • It also helps to make sure that there are no potentially-overlapping subobjects (cf. https://eel.is/c++draft/intro.object#7). No (potentially zero-sized) base-classes, no member variables with attribute `no_­unique_­address`. And perhaps best to use a standard-layout class (https://eel.is/c++draft/class.prop#3). Then it should be definitely portable between compilers and compiler versions. – Sebastian Jan 06 '22 at 16:31
  • 1
    @Sebastian I don't see why any of those could be a problem if `has_unique_object_representations_v` returns true. – HolyBlackCat Jan 06 '22 at 16:34
  • The steps I mentioned help to ensure the object representation is unique. `has_unique_object_representations_v` only works at compile time (in comparison to design time of the code) and can break with code change or new compiler version. Better to understand, when it definitely works or is guaranteed to work. – Sebastian Jan 06 '22 at 17:18
  • @Sebastian Tbh, if it returned false, I probably wouldn't trust the compiler to keep the padding zeroed even if I don't do anything you listed. I'd try to figure out where exactly the padding is, and ignore it when comparing. – HolyBlackCat Jan 06 '22 at 19:34
  • Of course, should be in no way a replacement for `has_unique_object_representation_v`, but a consideration for the design phase. I was talking about 'helping steps', not about alternative. If it returned `false` (and it *should* be definitely statically checked), that should be a no-go. Then unsuitable classes were used. – Sebastian Jan 06 '22 at 20:14