каст ребенка к родителю не работает в адресах полей класса
От: m.e.  
Дата: 02.04.12 02:01
Оценка:
идея была в том, чтобы сказать "а отсортируйка-ты записи вот по этому полю", но получился облом

struct Comparable {};
struct Child1: public Comparable {};
struct Child2: public Comparable {};

struct DatabaseRecord {
    Child1* field1;
    Child2* field2;
    void sort_by(Comparable* DatabaseRecord::* field) { /* ... */ }
};


int random() {/*...*/ }

int main() {
    DatabaseRecord table; /// чтобы упростить -- table состоит у нас ровно из одной DatabaseRecord

    Comparable* DatabaseRecord::* f1 = &DatabaseRecord::field1; // error: invalid conversion from `Child1* DatabaseRecord::*' to `Comparable* DatabaseRecord::*'
    Comparable* DatabaseRecord::* f2 = &DatabaseRecord::field2; // error: invalid conversion from `Child2* DatabaseRecord::*' to `Comparable* DatabaseRecord::*'

    Comparable* DatabaseRecord::* f  = random()>0 ? f1 : f2 ;
    table.sort_by( f );

    return 0;
}


вопрос в том, почему такой каст не разрешен — им что-то можно сломать? но что?

p.s. g++ (Debian 4.3.2-1.1) 4.3.2
Re: каст ребенка к родителю не работает в адресах полей клас
От: Caracrist https://1pwd.org/
Дата: 02.04.12 05:39
Оценка:
Здравствуйте, m.e., Вы писали:

ME>вопрос в том, почему такой каст не разрешен — им что-то можно сломать? но что?


Ссылка на поле, это по сути отступ.
Child * Agregator::* childOffset = &Agregator::field; // допустим тут отступ = 0
Base * Agregator::* childOffset2 = childOffset; // поле то же, и соответственно отступ = 0


однако, если:
struct Base {};
struct Holder { int Place; };
struct Child : Holder, Base {};
struct Agregator { Child * field; };

При касте, Child* => Base* значение указателя меняется...

int main()
{
  Agregator obj = { new Child() }; // obj.field = 0x00120328

  Child * Agregator::* childOffset = &Agregator::field; // childOffset = 0
  Base * Agregator::* childOffset2 = childOffset; // childOffset2 = 0

  Child * accAsChild = obj.field; // accAsChild = 0x00120328
  Base * accAsBase = obj.field; // accAsBase = 0x0012032C ~возможно другое значение

  Base * accAsBaseOffset = obj.*childOffset2; // accAsBaseOffset = 0x00120328
  assert(accAsBase == accAsBaseOffset);
}
~~~~~
~lol~~
~~~ Single Password Solution
Re: каст ребенка к родителю не работает в адресах полей клас
От: Caracrist https://1pwd.org/
Дата: 02.04.12 05:56
Оценка:
Здравствуйте, m.e., Вы писали:

ME>вопрос в том, почему такой каст не разрешен — им что-то можно сломать? но что?


Мне интересно другое:
struct Base {};
struct Holder { int Place; };
struct Child : Holder, Base {};
struct Agregator { Child field; };

int main() {
    Agregator obj;
    Base * ptr = &obj.field; // ок
    Base Agregator ::* offset = &Agregator::field; //invalid conversion from 'Child Agregator::*' to 'Base Agregator::*'
    return 0;
}

Почему такое не разрешено? Я понимаю что виртуальное неследование (и dynamic_cast) это не поддерживает, но во всех остальных случаях вполне может работать...

Вот в этом случае же сделать разделение смогли:
struct Base1 { int field;};
struct Child1 : Base1 {};

struct Base2 { int field;};
struct Child2 : virtual Base2 {};

int main() {
    int Child1::* offset1 = &Base1::field; // ok
    int Child2::* offset2 = &Base2::field; // pointer to member conversion via virtual base 'Base2'
    return 0;
}
~~~~~
~lol~~
~~~ Single Password Solution
Re: каст ребенка к родителю не работает в адресах полей клас
От: Centaur Россия  
Дата: 02.04.12 07:38
Оценка: :)
Здравствуйте, m.e., Вы писали:

>
ME>struct Comparable {};
ME>struct Child1: public Comparable {};

ME>    Comparable* DatabaseRecord::* f1 = &DatabaseRecord::field1; // error: invalid conversion from `Child1* DatabaseRecord::*' to `Comparable* DatabaseRecord::*'


ME>вопрос в том, почему такой каст не разрешен — им что-то можно сломать? но что?


По той же причине, почему не разрешён каст из Derived** в Base**, с минимальными модификациями контрпримера.
class Vehicle {
public:
  virtual ~Vehicle() { }
  virtual void startEngine() = 0;
};

class Car : public Vehicle {
public:
  virtual void startEngine();
  virtual void openGasCap();
};

class NuclearSubmarine : public Vehicle {
public:
  virtual void startEngine();
  virtual void fireNuclearMissle();
};

struct VehiclePtrs { 
  Car* carPtr;
  NuclearSubmarine* subPtr;
}

int main()
{
  Vehicles vs;
  Car car;
  vehicles.carPtr = &car;
  Car* Vehicles::* carPtrPm = &Vehicles::carPtr;
  Vehicle* Vehicles::* vehiclePtrPm = carPtrPm;  // This is an error in C++
  NuclearSubmarine  sub;
  vehicles.subPtr = ⊂
  vehicles.*vehiclePtrPm = vehicles.subPtr;
  // This last line would have caused vehicles.carPtr to point to sub !
  vehicles.carPtr->openGasCap();  // This might call fireNuclearMissle()!
  ...
}
Re: каст ребенка к родителю не работает в адресах полей клас
От: Кодт Россия  
Дата: 02.04.12 08:15
Оценка:
Здравствуйте, m.e., Вы писали:

ME>вопрос в том, почему такой каст не разрешен — им что-то можно сломать? но что?


Если взглянуть на указатели как на специального рода функции, то поймём, в чём фокус ковариантности и контравариантности.
struct Base           { int x; void foo(char); };
struct Derived : Base { int y; void bar(char); };

Base b; Derived d;

// указатель на объект - это функция, возвращающая объект

Base& theB() { return b; }
Base& getB() { return d; } // можем получить под-объект базового типа (наследник является базой)
Derived& theD() { return d; }
Derived& badD() { return b; } // ошибка: база не является наследником

Base& (*getObj)() = rand() ? theB : getB;

// указатель на член - это функция, принимающая объект

int getBx(Base& obj) { return obj.x; }
int getBy(Base& obj) { return obj.y; } // ошибка: у базы нет такого члена
int getDx(Derived& obj) { return obj.x; } // можем получить член базы
int getDy(Derived& obj) { return obj.y; }

int (*getMember)(Derived&) = rand() ? getDx : getDy;

// указатель на функцию-член - это опять функция, принимающая объект + параметры этой функции

void callBfoo(Base& obj, char c) { obj.foo(c); }
void callBbar(Base& obj, char c) { obj.bar(c); } // ошибка: нет такого члена
void callDfoo(Derived& obj, char c) { obj.foo(c); } // можем вызвать член базы
void callDbar(Derived& obj, char c) { obj.bar(c); }

void (*callMember)(Derived&,char) = rand() ? callDfoo : callDbar;


Поэтому кастинг указателя-на-объект разрешён вверх (theD --> getB), а кастинг указателя-на-член — разрешён вниз (getBx --> getDx, callBfoo --> callDfoo).
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.