Как привести указатель переменной-члена к универсальному типу в C ++
-
05-07-2019 - |
Вопрос
В моем приложении есть код, подобный этому:
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);
Вопрос в том, каким должен быть MYSTERYTYPE? ни void *, ни int не работают, несмотря на то, что значение & amp; A :: b печатается просто отлично, если вы выводите его через printf.
Пояснения: Да, & amp; A :: b определено в C ++. Да, я пытаюсь получить смещение для члена класса. Да, я хитрый.
Редактировать: О, я могу использовать offsetof (). В любом случае, спасибо.
Решение
У вас есть указатель на элемент данных для двух не связанных классов. Ну, вы не можете найти общий тип, который может содержать оба указателя. Он будет работать только в том случае, если параметр функции является указателем на элемент данных для члена производного, поскольку он гарантированно будет содержать этот элемент, даже если база содержит его:
struct a { int c; }; struct b : a { }; int main() { int b::*d = &a::c; }
<Ч>
Обновление . Думаю, мне следует написать, почему приведенное выше значение неявно преобразуется из a::*
в b::*
. В конце концов, у нас обычно есть b*
до a*
! Рассмотрим:
struct a { };
struct b : a { int c; };
struct e : a { };
int main() { int a::*d = &b::c; e e_; (e_.*d) = 10; /* oops! */ }
Если бы вышеприведенное было верным, вы бы сильно облажались. Вышеуказанное не действительно, поскольку преобразование из e
в b
не является неявным. Как видите, мы присваиваем указатель на b :: c, а затем можем разыменовать его, используя класс, который его вообще не содержит! (a
). Компилятор обеспечивает этот порядок:
int main() { int b::*d = &b::c; e e_; (e_.*d) = 10; /* bug! */ }
Теперь не удается скомпилировать, потому что <=> не является производным от <=>, к которому принадлежит указатель на член указателя. Хорошо! Следующее, однако, очень допустимо и, конечно, компилируется (измененные классы <=> и <=>):
struct a { int c; };
struct b : a { };
struct e : a { };
int main() { int e::*d = &a::c; e e_; (e_.*d) = 10; /* works! */ }
<Ч>
Чтобы это работало в вашем случае, вы должны сделать свою функцию шаблоном:
template<typename Class>
void DoThings (int Class::*arg) { /* do something with arg... */ }
Теперь компилятор автоматически определит правильный класс, которому принадлежит данный указатель на член. Вам придется передать экземпляр вместе с указателем на член, чтобы фактически использовать его:
template<typename Class>
void DoThings (Class & t, int Class::*arg) {
/* do something with arg... */
(t.*arg) = 10;
}
Если вы просто хотите установить какого-то члена, которого вы уже знаете на момент написания DoThings, вам хватит следующего:
template<typename Class>
void DoThings (Class & t) {
t.c = 10;
}
Другие советы
Вы просто пытаетесь вызвать функцию с адресом целого числа, который находится внутри объекта A
или C
? В этом случае ответ Джеффа МакГлинна - путь.
В противном случае, если вы действительно пытаетесь сделать что-то хитрое, требующее странного средства указателя на член в C ++ (а вы почти наверняка этого не делаете):
Поскольку классы <=> и <=> не связаны, вам понадобится функция шаблона, чтобы обрабатывать оба:
template <typename T>
void DoThings(int T::*x);
Если <=> на самом деле получено из <=>, будет работать следующее:
void DoThings(int A::*x);
& amp; A :: b и & amp; C :: d не имеют смысла, связанный адрес отсутствует. Вы пытаетесь получить смещение члена?
Вы уверены, что не хотите что-то вроде следующего?
DoSomething(&obj_a,&obj_a.b);
Если вы используете шаблоны, как подсказывает j_random_hacker, и компилятор знает тип каждого класса в точке, где вы вызываете функцию, буквальный ответ на ваш вопрос - " template <typename CLASS> void DoThings (CLASS * object, int CLASS::*MEMBER)
" ;. р>
Вот как это вписалось бы в ваш пример:
#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;
}