Как привести указатель переменной-члена к универсальному типу в C ++

StackOverflow https://stackoverflow.com/questions/420726

Вопрос

В моем приложении есть код, подобный этому:

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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top