问题
I have code similar to this in my application:
class A
{
public: int b;
}
class C
{
public: int d;
}
void DoThings (void *arg1, MYSTERYTYPE arg2);
A obj_a;
C obj_c;
DoThings(&obj_a, &A::b);
DoThings(&obj_c, &C::d);
The question is - What should MYSTERYTYPE be? neither void* nor int work, despite the value &A::b being printed just fine if you output it through a printf.
Clarifications: Yes, &A::b is defined under C++. Yes, I am trying to get the offset to a class member. Yes, I am being tricky.
Edit: Oh I can use offsetof(). Thanks anyway.
回答1:
You have a data member pointer to two unrelated classes. Well, you can't find a common type that can hold both pointers. It will only work if the function parameter is a data member pointer to a member of the derived, because it's guaranteed to contain the member too, if a base contains it:
struct a { int c; }; struct b : a { }; int main() { int b::*d = &a::c; }
Update: I think i should write why the above converts from a::*
to b::*
implicitly. After all, we usually have b*
to a*
! Consider:
struct a { };
struct b : a { int c; };
struct e : a { };
int main() { int a::*d = &b::c; e e_; (e_.*d) = 10; /* oops! */ }
If the above would be valid, you would really much screw up. The above is not valid, because conversion from b::*
to a::*
is not implicit. As you see, we assigned a pointer to b::c, and then we could dereference it using a class that doesn't contain it at all! (e
). The compiler enforces this order:
int main() { int b::*d = &b::c; e e_; (e_.*d) = 10; /* bug! */ }
It fails to compile now, because e
is not derived from b
, the class the member pointer pointer belongs to. Good! The following, however, is very valid and compiles, of course (changed classes a
and b
):
struct a { int c; };
struct b : a { };
struct e : a { };
int main() { int e::*d = &a::c; e e_; (e_.*d) = 10; /* works! */ }
To make it work for your case, you have to make your function a template:
template<typename Class>
void DoThings (int Class::*arg) { /* do something with arg... */ }
Now, the compiler will auto-deduce the right class that the given member pointer belongs too. You will have to pass the instance alongside of the member pointer to actually make use of it:
template<typename Class>
void DoThings (Class & t, int Class::*arg) {
/* do something with arg... */
(t.*arg) = 10;
}
If you just want to set some member you already know at the time you write DoThings, the following suffices:
template<typename Class>
void DoThings (Class & t) {
t.c = 10;
}
回答2:
Are you simply trying to call a function with the address of an integer that happens to live inside an A
or a C
object? In that case, Jeff McGlynn's answer is the way to go.
Otherwise, if you really are trying to do something tricky requiring C++'s weird pointer-to-member facility (and you almost certainly aren't):
Since classes A
and C
are unrelated, you will need a template function to handle both:
template <typename T>
void DoThings(int T::*x);
If C
was actually derived from A
, the following would work:
void DoThings(int A::*x);
回答3:
&A::b and &C::d are nonsensical, there is no associated address. Are you trying to get the offset of the member?
Are you sure you don't want something like the following?
DoSomething(&obj_a,&obj_a.b);
回答4:
If you use templates as j_random_hacker suggests, and the compiler knows the type of each class at the point where you call the function, the literal answer to your question is "template <typename CLASS> void DoThings (CLASS * object, int CLASS::*MEMBER)
".
Here's how it would fit into your example:
#include <iostream>
class A {
public:
int b;
};
class C {
public:
int d;
};
template <typename CLASS>
void DoThings (CLASS * object, int CLASS::*MEMBER)
{
std::cout << object->*MEMBER << std::endl;
}
A obj_a = { 2 };
C obj_c = { 4 };
int main (int argc, const char * argv[])
{
DoThings(&obj_a, &A::b);
DoThings(&obj_c, &C::d);
return 0;
}
来源:https://stackoverflow.com/questions/420726/how-to-cast-member-variable-pointer-to-generic-type-in-c