Fly
Fly

Reputation: 51

compare a pointer to member?

I was confused about why can't compare pointers to member using binary operator<

class Point3d{
  protected:
      //..
 public:
     float x;
      static list<Point3d*> *freeList;
 public:
     float y;
     static const int chunkSize = 250;
 public:
    float z;

};
and a template:

template< class class_type, class data_type1, class data_type2 >

char* access_order(data_type1 class_type:: *mem1, data_type2 class_type:: *mem2)
{

    return
      mem1 < mem2 ?
         "member 1 accurs first":
         "member 2 accurs first";
}  

when I called the access_order like below:

access_order(&Point3d::z, &Point3d::y);

the g++ reported:

"invalid operands of types ‘float Point3d::*’ and ‘float Point3d::*’ to binary ‘operator<’"

Is there a way compare pointer to member, I mean the unequal comparison, and how?

Upvotes: 5

Views: 1348

Answers (6)

Pavel K.
Pavel K.

Reputation: 1073

One of the best option - make a raw copy via std::memcpy, calculate hash and then use it for comparison (thanks @HolyBlackCat for comments). The function below calculates the hash for passed pointer-to-member (tested on modern C++ 17 compilers VS, GCC. CLang).

#include <cstring>
#include <string_view>
#include <functional>

template <typename TObject, typename TMember>
size_t HashMemberPtr(TMember TObject::* memberPtr)
{
    char buf[sizeof memberPtr];
    std::memcpy(&buf, &memberPtr, sizeof memberPtr);
    return std::hash<std::string_view>{}(std::string_view(buf, sizeof buf));
}

Unfortunately it's not compatible with std::hash<> as last requires only one template argument.

How to use:

struct CPoint3D
{
    float x;
    float y;
    float z;
};

int main()
{
    const size_t xHash = HashMemberPtr(&CPoint3D::x);

    assert(xHash == HashMemberPtr(&CPoint3D::x));
    assert(xHash != HashMemberPtr(&CPoint3D::y));
    assert(xHash != HashMemberPtr(&CPoint3D::z));

    return 0;
}

Upvotes: 2

Florin C.
Florin C.

Reputation: 613

Pointer comparison seem to tempting everyone but they always lead to a non-portable or undefined behavior.

The easiest answer is that you should never do this. There is no computational problem that cannot be solved with classical approaches.

Don't get me wrong, I am aware that asking wrong questions might get interesting thoughts, or a build better understanding on how a language or even CPU works.

Upvotes: 0

Pete Becker
Pete Becker

Reputation: 76245

You can compare the addresses of the members of an object:

A a;
if (std::less<void*>()(&a.a, &a.b))
    std::cout << "a precedes b\n";
else
    std::cout << "a follows b\n";

Upvotes: 2

Lol4t0
Lol4t0

Reputation: 12547

Pointers to members do not point to some memory themselves. They are just labels. The only thing you can do with them is to convert them to reference to pointee value of the given object with operator .* or ->* or store in another pointer to member variable.

struct A
{
    int a;
    float b;
};
A a;
int A::* p2m = &A::a;
int A::* p2m2 = p2m;

int & realPointer = a.*p2m;

Note, that you only can compare pointers of the same type, so you can't compare pointer to A::a (int A::*) with pointer to A::b (float A::*)

Upvotes: 0

James Kanze
James Kanze

Reputation: 153909

For the same reason you can't compare pointers in general. The only comparisons for order that are supported is if two data pointers point into the same array. Otherwise, the results of the comparison are unspecified; a compiler is not required to make the operators "work" in any reasonable way. (Ensuring a total order here would require extra computation on some architectures.) Since there's no case you can get specified results for a pointer to member, the standard doesn't allow them as arguments to the operators.

If you need total ordering, std::less et al. is guaranteed to provide it. Including, if I understand the standard correctly, member pointers. (Although providing a total ordering for pointer to member functions would probably be very expensive.) Even then, however, this ordering may be arbitrary; it would certainly not be required to reflect any ordering in memory.

Upvotes: 1

AbdElRaheim
AbdElRaheim

Reputation: 1394

If the Point3d overloads < then deference them and do the compare,

return *mem1 < *mem2 ? "member 1 accurs first": "member 2 accurs first";

or change the signature

char* access_order(data_type1 class_type:: &mem1, data_type2 class_type:: &mem2) char* access_order(data_type1 class_type:: mem1, data_type2 class_type:: mem2)

Did you want to do the compare on the actual memory address?

Upvotes: 0

Related Questions