идея была в том, чтобы сказать "а отсортируйка-ты записи вот по этому полю", но получился облом
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
Здравствуйте, 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);
}
Здравствуйте, 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;
}
Здравствуйте, 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()!
...
}
Здравствуйте, 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).