Come trasmettere il puntatore della variabile membro al tipo generico in C ++
-
05-07-2019 - |
Domanda
Ho un codice simile a questo nella mia applicazione:
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);
La domanda è: cosa dovrebbe essere MYSTERYTYPE? né void * né int funzionano, nonostante il valore & amp; A :: b sia stampato bene se lo si stampa in outputf.
Chiarimenti: Sì, & amp; A :: b è definito in C ++. Sì, sto cercando di ottenere l'offset per un membro della classe. Sì, sono complicato.
Modifica: Oh, posso usare offsetof (). Grazie comunque.
Soluzione
Hai un puntatore del membro dati a due classi non correlate. Bene, non riesci a trovare un tipo comune che può contenere entrambi i puntatori. Funzionerà solo se il parametro della funzione è un puntatore del membro dati a un membro del derivato, perché è garantito che contenga anche il membro, se una base lo contiene:
struct a { int c; }; struct b : a { }; int main() { int b::*d = &a::c; }
Aggiorna : penso che dovrei scrivere perché quanto sopra converte implicitamente da a::*
a b::*
. Dopotutto, di solito abbiamo da b*
a a*
! Prendere in considerazione:
struct a { };
struct b : a { int c; };
struct e : a { };
int main() { int a::*d = &b::c; e e_; (e_.*d) = 10; /* oops! */ }
Se quanto sopra fosse valido, potresti davvero rovinare tutto. Quanto sopra è non valido, poiché la conversione da e
a b
non è implicita. Come vedi, abbiamo assegnato un puntatore a b :: c, e quindi potremmo dereferenziarlo usando una classe che non lo contiene affatto! (a
). Il compilatore applica questo ordine:
int main() { int b::*d = &b::c; e e_; (e_.*d) = 10; /* bug! */ }
non riesce a compilare ora, poiché <=> non deriva da <=>, la classe a cui appartiene il puntatore del puntatore del membro. Buono! Quanto segue, tuttavia, è molto valido e compila, ovviamente (classi cambiate <=> e <=>):
struct a { int c; };
struct b : a { };
struct e : a { };
int main() { int e::*d = &a::c; e e_; (e_.*d) = 10; /* works! */ }
Per farlo funzionare nel tuo caso, devi rendere la tua funzione un modello:
template<typename Class>
void DoThings (int Class::*arg) { /* do something with arg... */ }
Ora, il compilatore dedurrà automaticamente la classe giusta a cui appartiene anche il puntatore del membro specificato. Dovrai passare l'istanza a fianco del puntatore del membro per utilizzarla effettivamente:
template<typename Class>
void DoThings (Class & t, int Class::*arg) {
/* do something with arg... */
(t.*arg) = 10;
}
Se vuoi solo impostare un membro che già conosci al momento in cui scrivi DoThings, è sufficiente:
template<typename Class>
void DoThings (Class & t) {
t.c = 10;
}
Altri suggerimenti
Stai semplicemente cercando di chiamare una funzione con l'indirizzo di un numero intero che si trova all'interno di un oggetto A
o C
? In tal caso, la risposta di Jeff McGlynn è la strada da percorrere.
Altrimenti, se stai davvero cercando di fare qualcosa di complicato che richiede la strana struttura da puntatore a membro di C ++ (e quasi sicuramente non lo sei):
Poiché le classi <=> e <=> non sono correlate, sarà necessaria una funzione modello per gestire entrambi:
template <typename T>
void DoThings(int T::*x);
Se <=> fosse effettivamente derivato da <=>, funzionerebbe quanto segue:
void DoThings(int A::*x);
& amp; A :: b e & amp; C :: d sono privi di senso, non vi è alcun indirizzo associato. Stai cercando di ottenere l'offset del membro?
Sei sicuro di non voler qualcosa di simile al seguente?
DoSomething(&obj_a,&obj_a.b);
Se usi i template come suggerisce j_random_hacker e il compilatore conosce il tipo di ogni classe nel punto in cui chiami la funzione, la risposta letterale alla tua domanda è " template <typename CLASS> void DoThings (CLASS * object, int CLASS::*MEMBER)
" ;.
Ecco come si adatterebbe al tuo esempio:
#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;
}