Как привести указатель переменной-члена к универсальному типу в C ++ - PullRequest
2 голосов
/ 07 января 2009

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

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 не работают, несмотря на то, что значение & A :: b печатается просто отлично, если вы выводите его через printf.

Пояснения: Да, & A :: b определено в C ++. Да, я пытаюсь получить смещение для члена класса. Да, я хитрый.

Редактировать: О, я могу использовать offsetof (). В любом случае, спасибо.

Ответы [ 4 ]

6 голосов
/ 07 января 2009

У вас есть указатель на элемент данных для двух не связанных классов. Ну, вы не можете найти общий тип, который может содержать оба указателя. Он будет работать только в том случае, если параметр функции является указателем члена данных на член производного, потому что он гарантированно будет содержать член, даже если база содержит его:

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! */ }

Если бы вышеприведенное было бы справедливо, вы бы сильно облажались. Выше указано , а не , поскольку преобразование из b::* в a::* не является неявным. Как видите, мы присваиваем указатель на b :: c, а затем можем разыменовать его, используя класс, который его вообще не содержит! (e). Компилятор применяет этот порядок:

int main() { int b::*d = &b::c; e e_; (e_.*d) = 10; /* bug! */ }

Он не может скомпилировать сейчас, потому что e не является производным от b, класса, к которому принадлежит указатель на член указателя. Хорошо! Следующее, однако, очень допустимо и, конечно, компилируется (измененные классы a и b):

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;
}
2 голосов
/ 07 января 2009

Вы просто пытаетесь вызвать функцию с адресом целого числа, который находится внутри объекта A или C? В этом случае ответ Джеффа МакГлинна - путь.

В противном случае, если вы действительно пытаетесь сделать что-то хитрое, требующее странного средства указателя на член в C ++ (а вы, скорее всего, нет):

Поскольку классы A и C не связаны, вам понадобится функция шаблона для обработки обоих типов:

template <typename T>
void DoThings(int T::*x);

Если бы C был фактически получен из A, сработало бы следующее:

void DoThings(int A::*x);
0 голосов
/ 05 мая 2011

Если вы используете шаблоны, как подсказывает 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;
}
0 голосов
/ 07 января 2009

& A :: b и & C :: d не имеют смысла, связанный адрес отсутствует. Вы пытаетесь получить смещение члена?

Вы уверены, что не хотите что-то вроде следующего?

DoSomething(&obj_a,&obj_a.b);
...