Самоуверенность заключается в том, что ты отсылаешь авторитета за справочником. Новые операторы приведения были одобрены на заседании комитета в Сан-Хосе, в ноябре 1993 года.
АТ>Если мое утверждение согласуется со стандартом языка, а утверждение Steve Clamage ему противоречит, то я прав, а он — нет. Тут все просто.
Ты так ничего и не понял. Проблема не в том, как заставить компилятор замолчать (это можно сделать почти всегда), но в том, чтобы предвидеть проблемы, которые могут возникнуть во время исполнения (см. Струаструп).
АТ>А вот и выдержка из стандарта языка:
АТ>5.2.9 Static cast АТ>... АТ>9 An rvalue of type "pointer to member of D of type cv1 T", can be converted to an rvalue of type "pointer to member of B of type cv2 T", where B is a base class of D, if a valid standard conversion from "pointer to member of B of type T" to "pointer to member of D of type T" exists, and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1....
АТ>Вопрос закрыт.
Для самоуверенных слепцов. Ты не привел [Замечание] из того же параграфа 9 части 5.2.9 стандарта, оно гласит:
[Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ]
В свою очередь, в параграфе 4 пункта 5.5 сказано:
If the dynamic type of the object does not contain the member to which the pointer refers, the behavior is undefined.
возвращаясь к примеру в сообщении, породившему это дерево, имеем:
B* b = &TestC;
здесь b указывает на подобъект B класса C. У класса B нет члена, на который указывает pf. Этого требует известный (или нет?) тебе стандарт. Все работает верно и в случае с
B* b = static_cast<B*>((void*)&TestC)
.
АТ>А это совершенно не имеет никакого значения. Еще раз — спецификация языка С++ — объективна. И истинность утверждений в этой области определяется только из соответствия утверждения этой спецификации. Популярность имени утверждаюшего и его личный авторитет никакого значения не имеют.
Это имеет значение. Пержде, чем начать критиковать авторитет, надо долго думать, затем еще раз думать. И если (что маловероятно) тебе покажется, что ты прав, тогда задумайся: где ты ошибся.
АТ>И не надо совать мне в нос сравнения моего авторитета с авторитетом Стива Клэмиджа. Я не могу участвовать в проведении такого сравнения из соображений обычной порядочности.
Ну это понятно. А если учесть, что сам Стив Клэмидж не принимает в этом участие, то станет очевидным КПД подобного занятия.
АТ>Это потому, что ты еще не научился читать такие книги. В С++, как и во многих языках программирования, есть как минимум два типа ституации "нельзя так делать":
Вот, когда через много лет, ты напишешь свою книгу, мы поговорим о том, как ее надо уметь читать.
P.S.
Может для кого-то это покажется маловажным, но код, выдаваемый компилятором Borland в примере с TestC, дает те же результаты, что и код, выдаваемый MSVC++.
Re[7]: Множественное наследование: разминка для мозгов
Здравствуйте Pavel Ivlev, Вы писали:
PI>Самоуверенность заключается в том, что ты отсылаешь авторитета за справочником. Новые операторы приведения были одобрены на заседании комитета в Сан-Хосе, в ноябре 1993 года.
АТ>>Если мое утверждение согласуется со стандартом языка, а утверждение Steve Clamage ему противоречит, то я прав, а он — нет. Тут все просто.
PI>Ты так ничего и не понял. Проблема не в том, как заставить компилятор замолчать (это можно сделать почти всегда), но в том, чтобы предвидеть проблемы, которые могут возникнуть во время исполнения (см. Струаструп).
А зачем ты сейчас об этом говоришь? С этим никто не спорит и об этом речь не идет. Стив в своем ответе сделал вполне конкретное безапелляционное заявление "This is not allowed". Это не так.
АТ>>А вот и выдержка из стандарта языка:
АТ>>5.2.9 Static cast АТ>>... АТ>>9 An rvalue of type "pointer to member of D of type cv1 T", can be converted to an rvalue of type "pointer to member of B of type cv2 T", where B is a base class of D, if a valid standard conversion from "pointer to member of B of type T" to "pointer to member of D of type T" exists, and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1....
АТ>>Вопрос закрыт.
PI>Для самоуверенных слепцов. Ты не привел [Замечание] из того же параграфа 9 части 5.2.9 стандарта, оно гласит: PI>[Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ]
А не надо "переводить стрелки". Это замечание я здесь уже не раз приводил (тема указателей на методы в множественном наследовании тянутся тут уже долго). Но в данном конкретном случае (сообщение Стива Клэмиджа) оно ничего не меняет. В примере, который содержится в вопросе, динамический тип объекта корректен и никаких проблем не возникает.
PI>В свою очередь, в параграфе 4 пункта 5.5 сказано: PI>If the dynamic type of the object does not contain the member to which the pointer refers, the behavior is undefined.
Ты понимаешь, что такое 'dynamic type'? Я вижу, что не понимаешь.
PI>возвращаясь к примеру в сообщении, породившему это дерево, имеем:
PI>
B* b = &TestC;
PI>здесь b указывает на подобъект B класса C. У класса B нет члена, на который указывает pf. Этого требует известный (или нет?) тебе стандарт.
В переводе на язык С++ "b указывает на подобъект B класса C" означает, что статический тип объекта '*b' равен B, а динамический тип объекта '*b' равен C. У класса С есть член, на который указывет 'pf', поэтому все требования стандарта тут строго соблюдены.
Если у тебя этот пример "не работает", это означает, что твой компилятор глючит. Здесь уже подробено объяснялось, почему такое может происходить в MSVC++. Поставь правильные установки компиляцмии, и все заработает. В GCC все это прекравно работает сразу.
PI> Все работает верно и в случае с
B* b = static_cast<B*>((void*)&TestC)
.
Вот это и есть пример кода, который ни в коем случае нельзя использовать в своих программах. В таком случае, если что-то и "работает", то по чисто случайным причинам. Уже указывалось, что если поменять порядок следования баз класса С, то он перестанет "работать". Это во-первых. А во-вторых, стандарт С++ вообше не гарантирует, что хотя бы одна из баз класса имеет тот же адрес, что и сам экземпляр класса. Этот код дает undefined behavior.
АТ>>Это потому, что ты еще не научился читать такие книги. В С++, как и во многих языках программирования, есть как минимум два типа ституации "нельзя так делать":
PI>P.S. PI>Может для кого-то это покажется маловажным, но код, выдаваемый компилятором Borland в примере с TestC, дает те же результаты, что и код, выдаваемый MSVC++.
У тебя работоспособность этого примера у MSVC++ "зарублена" неправильными установками компиляции. Поставь правильные — и код перестанет "совпадать". Я не знаю, есть ли у Борланда установки, позволяющие заставить работать этот пример.
Best regards,
Андрей Тарасевич
Re[7]: Множественное наследование: разминка для мозгов
Здравствуйте Pavel Ivlev, Вы писали:
PI>Самоуверенность заключается в том, что ты отсылаешь авторитета за справочником. Новые операторы приведения были одобрены на заседании комитета в Сан-Хосе, в ноябре 1993 года.
Операторы приведения — одно, спецификация этих операторов — другое. Класс 'std::auto_ptr' тоже был введен в состав STL давно. А вот его спецификация устоялась только в последний момент перед выходом стандарта.
А даже если нынешняя спецификация была принята еще в 1993, то это означает, что в 1995 Стив Клэмидж был просто напросто не прав, говоря, что "It is not allowed".
Best regards,
Андрей Тарасевич
Re[8]: Множественное наследование: разминка для мозгов
Здравствуйте Андрей Тарасевич, Вы писали:
PI>>Самоуверенность заключается в том, что ты отсылаешь авторитета за справочником. Новые операторы приведения были одобрены на заседании комитета в Сан-Хосе, в ноябре 1993 года.
АТ>Операторы приведения — одно, спецификация этих операторов — другое. Класс 'std::auto_ptr' тоже был введен в состав STL давно. А вот его спецификация устоялась только в последний момент перед выходом стандарта.
АТ>А даже если нынешняя спецификация была принята еще в 1993, то это означает, что в 1995 Стив Клэмидж был просто напросто не прав, говоря, что "It is not allowed".
А вот тебе и подтверждение того, что я был прав. Возьмем первый draft стандарта С++ (28 апреля 1995) и посмотрим на соответствующий пункт там:
5.2.8 Static cast
[...]
9 An rvalue of type "pointer to member of D of type cv1 T" can be converted to an rvalue of type "pointer to member of B of type cv2 T", where B is a base class of D, if a valid standard conversion from "pointer to member of B of type cv2 T" to "pointer to member of D of type cv2 T" exists, and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [...] If class B contains or inherits the original member, the resulting pointer to member points to the member in class B. Otherwise, the result of the cast is undefined.
(Сравни выдеделенные строки с соотв. строками финальной версии стандарта или даже второго драфта)
Надеюсь теперь тебе понятно, на что опирался Стив Клэмидж, отвечая на тот вопрос в 1995 году?
Лирическое отступление: тебе, на твоем уровне владения С++, еше рановато выступать в публичных С++ форумах с наездами на других людей. Особенно на тех, кто разбирается в вопросе лучше тебя. Более того, тебе пока еще рановато приводить в подтверждение своих утверждений цитаты от авторитетов С++, потому что ты пока не в состоянии правильно понять что именно они говорят и почему они это говорят.
Best regards,
Андрей Тарасевич
Re[9]: Множественное наследование: разминка для мозгов
Здравствуйте Андрей Тарасевич, Вы писали:
АТ>Лирическое отступление: тебе, на твоем уровне владения С++, еше рановато выступать в публичных С++ форумах с наездами на других людей. Особенно на тех, кто разбирается в вопросе лучше тебя. Более того, тебе пока еще рановато приводить в подтверждение своих утверждений цитаты от авторитетов С++, потому что ты пока не в состоянии правильно понять что именно они говорят и почему они это говорят.
Андрей, пожалуйста, не надо переносить методы ведения дискуссии R.Z1 сюда. Спасибо.
-- Alex Fedotov
Re[10]: Множественное наследование: разминка для мозгов
Здравствуйте Андрей Тарасевич, Вы писали:
АТ>А зачем ты сейчас об этом говоришь? С этим никто не спорит и об этом речь не идет. Стив в своем ответе сделал вполне конкретное безапелляционное заявление "This is not allowed". Это не так.
Я привел сообщение Стива Клэмиджа в ответ твоему заявлению о неработоспособности компилятора в примере с TestC. "This is not allowed",- это так! хотя бы потому что компилятор это не пропускает. Это также "not allowed", как и не допустимо изменение константной строки. Но ты можешь пойти в обход системы типов и компилятор позволит тебе это сделать.
АТ>А не надо "переводить стрелки". Это замечание я здесь уже не раз приводил (тема указателей на методы в множественном наследовании тянутся тут уже долго). Но в данном конкретном случае (сообщение Стива Клэмиджа) оно ничего не меняет. В примере, который содержится в вопросе, динамический тип объекта корректен и никаких проблем не возникает.
Я стараюсь быть прямолинейным, насколько это возможно. Если ты не понял, для чего я привел Стива Клэмиджа, возможно я виноват.
АТ>Ты понимаешь, что такое 'dynamic type'? Я вижу, что не понимаешь.
Самовлюленность тебя ослепляет.
АТ>В переводе на язык С++ "b указывает на подобъект B класса C" означает, что статический тип объекта '*b' равен B, а динамический тип объекта '*b' равен C. У класса С есть член, на который указывет 'pf', поэтому все требования стандарта тут строго соблюдены.
У тебя проблемы с переводом не только с английского языка: это верно в случае с lvalue. т.е.
B* b; C c; *b = c;
В случае с rvalue (наш случай) "The dynamic type of an rvalue expression is its static type."
АТ>Если у тебя этот пример "не работает", это означает, что твой компилятор глючит.
Это значит, что ты не понимаешь: почему он "не работает".
PI>> Все работает верно и в случае с
B* b = static_cast<B*>((void*)&TestC)
.
АТ>Вот это и есть пример кода, который ни в коем случае нельзя использовать в своих программах. В таком случае, если что-то и "работает", то по чисто случайным причинам. Уже указывалось, что если поменять порядок следования баз класса С, то он перестанет "работать". Это во-первых. А во-вторых, стандарт С++ вообше не гарантирует, что хотя бы одна из баз класса имеет тот же адрес, что и сам экземпляр класса. Этот код дает undefined behavior.
Этот код работает, потому что b начинает указывать на начало обекта TestC, не на подобъект B класса С. И, разумеется, не важен порядок следования базовых классов.
Re[8]: Множественное наследование: разминка для мозгов
Здравствуйте Андрей Тарасевич, Вы писали:
АТ>Операторы приведения — одно, спецификация этих операторов — другое. Класс 'std::auto_ptr' тоже был введен в состав STL давно. А вот его спецификация устоялась только в последний момент перед выходом стандарта.
Тебе просто нужно прочитать книгу Страуструпа D&E. Там ты узнаешь про работу комитета (глава 6.2).
Re[9]: Множественное наследование: разминка для мозгов
Здравствуйте Андрей Тарасевич, Вы писали:
АТ>А вот тебе и подтверждение того, что я был прав.
Лед тронулся.
AT>Возьмем первый draft стандарта С++ (28 апреля 1995) и посмотрим на соответствующий пункт там:
АТ>5.2.8 Static cast АТ>[...] АТ>9 An rvalue of type "pointer to member of D of type cv1 T" can be converted to an rvalue of type "pointer to member of B of type cv2 T", where B is a base class of D, if a valid standard conversion from "pointer to member of B of type cv2 T" to "pointer to member of D of type cv2 T" exists, and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [...] If class B contains or inherits the original member, the resulting pointer to member points to the member in class B. Otherwise, the result of the cast is undefined.
АТ>(Сравни выдеделенные строки с соотв. строками финальной версии стандарта или даже второго драфта)
Я незнаю: с чем ты это сравниваешь, но у меня стандарт от 1998-09-01, и там написано:
"An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to
member of B of type cv2 T”, where B is a base class (10) of D, if a valid standard conversion from “pointer
to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cvqualification
as, or greater cvqualification
than, cv1.59) The null member pointer value (4.11) is converted
to the null member pointer value of the destination type. If class B contains the original member, or is a
base or derived class of the class containing the original member, the resulting pointer to member points to
the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain
the original member, the dynamic type of the object on which the pointer to member is dereferenced
must contain the original member; see 5.5. ]"
По-твоему семантика различна?
АТ>Лирическое отступление: тебе, на твоем уровне владения С++, еше рановато выступать в публичных С++ форумах с наездами на других людей. Особенно на тех, кто разбирается в вопросе лучше тебя. Более того, тебе пока еще рановато приводить в подтверждение своих утверждений цитаты от авторитетов С++, потому что ты пока не в состоянии правильно понять что именно они говорят и почему они это говорят.
С поэзией у тебя не так хорошо, как с самовлюбленностью.
Re[10]: Множественное наследование: разминка для мозгов
Здравствуйте Pavel Ivlev, Вы писали:
AT>>Возьмем первый draft стандарта С++ (28 апреля 1995) и посмотрим на соответствующий пункт там:
АТ>>5.2.8 Static cast АТ>>[...] АТ>>9 An rvalue of type "pointer to member of D of type cv1 T" can be converted to an rvalue of type "pointer to member of B of type cv2 T", where B is a base class of D, if a valid standard conversion from "pointer to member of B of type cv2 T" to "pointer to member of D of type cv2 T" exists, and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [...] If class B contains or inherits the original member, the resulting pointer to member points to the member in class B. Otherwise, the result of the cast is undefined.
АТ>>(Сравни выдеделенные строки с соотв. строками финальной версии стандарта или даже второго драфта)
PI>Я незнаю: с чем ты это сравниваешь, но у меня стандарт от 1998-09-01, и там написано: PI>"An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to PI>member of B of type cv2 T”, where B is a base class (10) of D, if a valid standard conversion from “pointer PI>to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cvqualification PI>as, or greater cvqualification PI>than, cv1.59) The null member pointer value (4.11) is converted PI>to the null member pointer value of the destination type. If class B contains the original member, or is a PI>base or derived class of the class containing the original member, the resulting pointer to member points to PI>the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain PI>the original member, the dynamic type of the object on which the pointer to member is dereferenced PI>must contain the original member; see 5.5. ]"
PI>По-твоему семантика различна?
Открой глаза! Ты не видишь разницы??? Семантика принципиально различна! (Самое время припомнить твое "для тех, кто не ладит с английским")
Draft:
If class B contains or inherits the original member, the resulting pointer to member points to the member in class B. Otherwise, the result of the cast is undefined.
Final:
If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ]
В драфте требуется наличие данного члена в классе B (явно или унаследованно). В финальной версии его наличие в классе B не требуется, вместо этого тебуется его наличие в динамическом типе указуемого объекта.
С точки зрения драфта, Стив Клэмидж в своем ответе был совершенно прав. С точки зрения финальной версии — нет.
Best regards,
Андрей Тарасевич
Re[9]: Множественное наследование: разминка для мозгов
Здравствуйте Pavel Ivlev, Вы писали:
АТ>>А зачем ты сейчас об этом говоришь? С этим никто не спорит и об этом речь не идет. Стив в своем ответе сделал вполне конкретное безапелляционное заявление "This is not allowed". Это не так.
PI>Я привел сообщение Стива Клэмиджа в ответ твоему заявлению о неработоспособности компилятора в примере с TestC. "This is not allowed",- это так! хотя бы потому что компилятор это не пропускает. Это также "not allowed", как и не допустимо изменение константной строки. Но ты можешь пойти в обход системы типов и компилятор позволит тебе это сделать.
Нет. Попытка изменения константной строки приводит к неопределенному поведению. Это действительно not allowed. А приведние типа, о котром мы говорим, не только разрешено, но еше и четко определено (при соблюдении ряда условий).
Ты сам привел цитату из Страуструпа (и Клэмиджа) о том, что ситуация с указателями на методы в чем-то обратна ситуации с классами (у Страуструпа это называется contravariance). Приведение типа от указателя на потомка к указателю на предка (вверх по иерархии) является стадартной конверсией в C++. Для указателей на методы наоборот — стандартной конверсией является приведние типа вниз по иерархии. Эти конверсии могут выполняться неявно.
Тем не менее в С++ при помощи 'static_cast' можно сделать приведения и в обратном направлении. В С++ можно сделать так:
class A {};
class B : public A {};
...
A* pa = new B(); // standard conversion - upcast
B* pb = static_cast<B*>(pa); // static_cast, perfectly legal, downcast
С указателями на методы ситуация соврешенно аналогична. Чтобы сделать приведение типа такого указателя вверх по иерархии нало применить 'static_cast'.
class A { public: void foo(); };
class B : public A { public: int bar(int); };
void (B::*p1)() = &A::foo; // standard conversionint (A::*p2)(int) = static_cast<int (A::*)(int)>(&B::bar); // static_cast, perfectly legal
Ты говоришь, что такие приведения типов нелегальны? Неверно. Они совершенно легальны (если соблюдены требования стандарта).
Ты говоришь, что такие приведения типов опасны? Да, они могут быть опасны, если ими неправильно пользоваться. Операция деления тоже опасна — можно случайно что-нибудь на 0 разделить.
Ты говоришь, что это тоже самое, что модификация константной строки? Неверно. Модификация константной строки безусловно приводит к underfined behavior. А при таком приведении underfined behavior возникнет, только если нарушены условия, делающие такое приведение безопасным. В наших примерах эти условия пока соблюдались (как и в примере, на который отвечал Стив Клэмидж).
АТ>>В переводе на язык С++ "b указывает на подобъект B класса C" означает, что статический тип объекта '*b' равен B, а динамический тип объекта '*b' равен C. У класса С есть член, на который указывет 'pf', поэтому все требования стандарта тут строго соблюдены.
PI>У тебя проблемы с переводом не только с английского языка: это верно в случае с lvalue. т.е. PI>B* b; C c; *b = c; PI>В случае с rvalue (наш случай) "The dynamic type of an rvalue expression is its static type."
Ха ха ха! Ты разбираешься в данном вопросе даже меньше, чем я предполагал. Где ты в нашем случае увидел rvalue? Мы работаем с указателями. Запомни раз и навсегда: если 'b' — это указатель, то '*b' — это lvalue. Из этого правила нет исключений. Это и есть наш случай.
Прочитай определение dynamic type, там как раз наш пример и приведен
1.3.3. dynamic type
...
[Example: if a pointer 'p' whose static type is "pointer to class B" is pointing to an object of class D, derived from B, the dynamic type of the expression '*p' is D.]
АТ>>Если у тебя этот пример "не работает", это означает, что твой компилятор глючит. PI>Это значит, что ты не понимаешь: почему он "не работает".
Детский сад...
PI>>> Все работает верно и в случае с
B* b = static_cast<B*>((void*)&TestC)
.
АТ>>Вот это и есть пример кода, который ни в коем случае нельзя использовать в своих программах. В таком случае, если что-то и "работает", то по чисто случайным причинам. Уже указывалось, что если поменять порядок следования баз класса С, то он перестанет "работать". Это во-первых. А во-вторых, стандарт С++ вообше не гарантирует, что хотя бы одна из баз класса имеет тот же адрес, что и сам экземпляр класса. Этот код дает undefined behavior.
PI>Этот код работает, потому что b начинает указывать на начало обекта TestC, не на подобъект B класса С. И, разумеется, не важен порядок следования базовых классов.
Нет, это код начинает работать потому, что в данном случае глюк компилятора компенсируется глюком программиста. А в обшем случае он не работает. Попробуй на GCC или просто вставь в исходник для MSVC++ 6 вот такое
Здравствуйте Alex Fedotov, Вы писали:
AF>Андрей, пожалуйста, не надо переносить методы ведения дискуссии R.Z1 сюда. Спасибо.
Честно говоря, я бы лучше порекомендовал поучится дискутировать Павлу. Хотя, судя по его 18 постингам, большая половина из которых явно с понтами, это бесполезно. В общем, ещё одно юное дарование.
ЗЫ. Павел, я конечно понимаю, что Вы (можно мне на 'ВЫ') балшой специалист по чтению книжек и стандартов, и маленький в веб-дизайне, но не могли бы Вы подправить фоновую картинку на своей домашней страничке. Это совсем не трудно. А то выглядит это на моём древнем домашнем мониторчике весьма отстойно.
ЗЫ2. Андрей, take it easy, не стоит так болезненно реагировать на провокационные выпады вчерашних студентов. И уж тем более обкладывать их нулями. Много чести.
Если нам не помогут, то мы тоже никого не пощадим.
Re[11]: Множественное наследование: разминка для костей
Здравствуйте Андрей Тарасевич, Вы писали:
AF>>Андрей, пожалуйста, не надо переносить методы ведения дискуссии R.Z1 сюда. Спасибо.
АТ>В этой дискуссии, надо сказать, вышеупомянутые методы явственно проступили несколько раньше, чем я их сюда принес...
Предлагаю переименовать тему из "разминка для мозгов" в "разминка для костей" или же "промывка мозгов".
Have fun: Win+M, Ctrl+A, Enter
Re[12]: Множественное наследование: разминка для костей
Здравствуйте Андрей Тарасевич:
М>Предлагаю переименовать тему из "разминка для мозгов" в "разминка для костей" или же "промывка мозгов". :shuffle:
Пожалуйста, объясните механизмы приведения типов простым человечьим русским языком, не пересыпая английскими вставками из ДохлоПтица.
Так сказать, четко и по-военному.
Мне, к примеру, неочевидно, что возможны преобразования C -> B -> C и CFunc -> BFunc -> CFunc (с чего все началось). Продемонстрируйте, что ли, а то все на пальцАх...
(кстати, раз уж начал бередить прошлое)
получается, что танковый (thunk) код все же должен где-то быть:
в начале метода (проверка и приведение this к родному классу) — но это чертовски непродуктивно для статических вызовов
в месте вызова (pB->*pfnB)(); — а откуда понятно, что надо приводить именно к C, а не еще дальше (см. ниже)
в месте взятия ссылкок на метод: pfnB = (PFNB)fnC; и на объект: pB = &c;
class A { int m_a; public: A(): m_a(1) {} };
class B { int m_b; public: B(): m_b(2) {} };
class C: public A, public B
{
int m_c;
public:
C(): m_c(3) {}
void fnc(void) { assert(m_c == 3); };
};
class D { int m_d; public: D(): m_d(4) {} };
class E: public E, public C { int m_e; public: E(): m_e(5) {} };
typedef void (B::*PFNB)(void);
E e;
B* pb = &e; // pb == { here=(void*)&e + sizeof(D) + sizeof(A); origin=&e }
PFNB pfnb = (PFNB)(C::fnc);
(pb->*pfnb)(); // как вот здесь pb будет приведен именно к C, а не к E ?
Перекуём баги на фичи!
Re[13]: Множественное наследование: разминка для костей
Здравствуйте Кодт, Вы писали:
К>Пожалуйста, объясните механизмы приведения типов простым человечьим русским языком, не пересыпая английскими вставками из ДохлоПтица.
К>Так сказать, четко и по-военному.
или поменять соответствующую установку компиляции.
К>class A { int m_a; public: A(): m_a(1) {} };
К>class B { int m_b; public: B(): m_b(2) {} };
К>class C: public A, public B
К>{
К>int m_c;
К>public:
К> C(): m_c(3) {}
К> void fnc(void) { assert(m_c == 3); };
К>};
К>class D { int m_d; public: D(): m_d(4) {} };
К>class E: public E, public C { int m_e; public: E(): m_e(5) {} };
Имелось в виду, как я понимаю,
class E: public D, public C ...
К>typedef void (B::*PFNB)(void);
К>E e;
К>B* pb = &e; // pb == { here=(void*)&e + sizeof(D) + sizeof(A); origin=&e }
Здесь: 'pb' указывает на базовый подобъект типа 'B' в составе объекта 'е'.
К>PFNB pfnb = (PFNB)(C::fnc);
Правильно так:
PFNB pfnb = (PFNB)(&C::fnc);
Здесь образуется указатель на метод класса. Этот указатель в MSVC++ состоит из двух 32-хбитных полей: старшая часть — смещение this (будем обозначать словом 'offset'), младшая часть — собственно точка входа в метод (будем обозначать словом 'entry'). Рассмотрим создание этого указателя по шагам.
Сначала вычисляется '&C::fnc', т.е. создается собственно указатель типа 'void (C::*)()'. Это указатель будет иметь следующий вид
[entry = &C::fnc, offset = 0]
Затем выполняется приведение типа от 'void (C::*)()' к 'void (B::*)()'. Это приведение типа для такого указателя выполняется по следующему правилу: поле 'entry' остается таким как было, а из поля 'offset' вычитается смещение класса B (результирующий тип) в классе C (исходный тип). Это смещение в данном случае равно 4. Т.е. в результате приведения типа получается такой указатель
[entry = &C::fnc, offset = -4]
Все.
(pb->*pfnb)(); // как вот здесь pb будет приведен именно к C, а не к E ?
Вызов через такой указатель делается по следующему алгоритму:
1. Берется указатель на экземпляр класса и к нему прибавляется поле 'offset' указателя на метод. В нашем случае указатель 'pb' указывает на подобъект типа 'B' в составе объекта типа 'E', а поле 'offset' в 'pfnb' равно -4. Т.е. указатель на объект смещается на 4 байта назад. В результате он будет указывать на подобъект типа 'С' в составе объекта типа 'E'.
2. Делается обычный вызов метода через поле 'entry' указателя на метод. В качестве значения 'this' этому методу передается значение, вычисленное на шаге 1.
Все. Как видишь, все правильно работает. Метод 'C::fnc' получает указатель на объект именно типа 'C'.
Best regards,
Андрей Тарасевич
Re[14]: Множественное наследование: разминка для костей
(pb->>*pfnb)(); // как вот здесь pb будет приведен именно к C, а не к E ?
АТ>
АТ>Вызов через такой указатель делается по следующему алгоритму:
АТ>1. Берется указатель на экземпляр класса и к нему прибавляется поле 'offset' указателя на метод. В нашем случае указатель 'pb' указывает на подобъект типа 'B' в составе объекта типа 'E', а поле 'offset' в 'pfnb' равно -4. Т.е. указатель на объект смещается на 4 байта назад. В результате он будет указывать на подобъект типа 'С' в составе объекта типа 'E'.
АТ>2. Делается обычный вызов метода через поле 'entry' указателя на метод. В качестве значения 'this' этому методу передается значение, вычисленное на шаге 1.
Это алгоритм для даного конкретного случая. А в общем случае, при вызове метода через указатель 'px' типа 'void (X::*)()' с указателем на объект 'py' типа 'Y*'
(py->*px)();
делаются следующие шаги
1. Делается приведение типа указателя 'py' к типу 'X*'. При этом указатель на объект может сместиться.
2. К указателю, полученному на шаге 1, прибавляется поле 'offset' указателя 'px'. При этом указатель на объект может еще сместиться.
3. Делается обычный вызов метода через поле 'entry' указателя 'px'. В качестве значения 'this' этому методу передается значение, вычисленное на шаге 2.
В нашем примере шага 1 не было, потому что типы X и Y совпадали.
Best regards,
Андрей Тарасевич
Re: Множественное наследование: разминка для мозгов
Спасибо всем за участие. Я не ожидал такого энтузиазма.
Особое спасибо Андрею Тарасевичу и Pavel Ivlev за глубокие познания в области спецификации и истории С++. Хотя иногда эти исторические очерки и принимали слегка "разгоряченный" вид.(Кстати, кто это так усердно ставил Pavel Ivlev нули? )
Я рад, что поднятая мною тема оказалась полезна.
Всем спасибо
Re[2]: Множественное наследование: разминка для мозгов
Здравствуйте Roman Fadeyev, Вы писали:
RF>Спасибо всем за участие. Я не ожидал такого энтузиазма.
RF>Особое спасибо Андрею Тарасевичу и Pavel Ivlev за глубокие познания в области спецификации и истории С++. Хотя иногда эти исторические очерки и принимали слегка "разгоряченный" вид.(Кстати, кто это так усердно ставил Pavel Ivlev нули? )
RF>Я рад, что поднятая мною тема оказалась полезна.
RF>Всем спасибо
Ну что, прямая дорога в QnA.
Перекуём баги на фичи!
Re[2]: Множественное наследование: разминка для мозгов
Здравствуйте Roman Fadeyev, Вы писали:
RF>Спасибо всем за участие. Я не ожидал такого энтузиазма.
RF>Особое спасибо Андрею Тарасевичу и Pavel Ivlev за глубокие познания в области спецификации и истории С++. Хотя иногда эти исторические очерки и принимали слегка "разгоряченный" вид.
Учитывая тот факт, что я и Павел в течение данной дискусии придерживались противоположных точек зрения, такое общее "спасибо" мне кажется несколько ...э-э-э.... сбивающим с толку
RF>(Кстати, кто это так усердно ставил Pavel Ivlev нули? )
Нули ставил я. (Если ты откроешь интересующее тебя сообщение и нажмеш на оценку в заголовке, ты увидишь, кто конкретно ставил оценки). Нули я поставил именно на те сообщения Павла, которые содержать не соответствующую действительности информацию.
Best regards,
Андрей Тарасевич
Re[3]: Множественное наследование: разминка для мозгов
Здравствуйте Андрей Тарасевич, Вы писали:
АТ>Учитывая тот факт, что я и Павел в течение данной дискусии придерживались противоположных точек зрения, такое общее "спасибо" мне кажется несколько ...э-э-э.... сбивающим с толку
Да ладно Вам, не горячитесь, спор уже давно закончился, больше юмора, друг мой.
АТ>Нули ставил я. (Если ты откроешь интересующее тебя сообщение и нажмеш на оценку в заголовке, ты увидишь, кто конкретно ставил оценки).
Я знаю, это шутка. Видели там подмигивающую рожу? Вот такую: . Вот и чудненько, а теперь — по пивку.