Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, functional, Вы писали:
F>>Прямо говорится: К>
F>>...any direct or indirect call to an unimplemented pure virtual function from a constructor or destructor results in undefined behavior.
К>Прямой вызов виртуальной функции из ктора/дтора — статический. Если функция не определена (неважно, =0 она или нет), то получим не UB, а ошибку линковки.
Я встречал только один способ подавления виртуальности вызова в стандарте и это — явная квалификация 10.3 (12)
Если нет явной квалификации, вызов — виртуальный. А виртуальный вызов чисто виртуальной функции из ctor/dtor — undefined 10.4 (6)
Лазар
Re[9]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Лазар Бешкенадзе, Вы писали: ЛБ>Вот чего требует стандарт:
ЛБ>12.7 (3) ЛБ>... When a virtual function is called directly or indirectly from a constructor (including ЛБ>from the mem-initializer for a data-member) or from a destructor, and the object to which ЛБ>a call applies is the object under construction or destruction, the function called is the ЛБ>one defined in the constructor or destructor's own class or in one of its bases, ...
ЛБ>Покажи, где тут говорится о каком-либо соответствии.
То, что ты привел — и есть правила для вызова обычной функции, если ты этого не заметил
ЛБ>К тому же приведенный здесь пример
явно показывал несоответствие.
Он показал лишь то, что используемый тобой компилятор выполняет вызов в соответствии с требованием стандарта, и ничего более. ЛБ>А тут еще один пример
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Лазар Бешкенадзе, Вы писали: ЛБ>>Вот чего требует стандарт:
ЛБ>>12.7 (3) ЛБ>>... When a virtual function is called directly or indirectly from a constructor (including ЛБ>>from the mem-initializer for a data-member) or from a destructor, and the object to which ЛБ>>a call applies is the object under construction or destruction, the function called is the ЛБ>>one defined in the constructor or destructor's own class or in one of its bases, ...
ЛБ>>Покажи, где тут говорится о каком-либо соответствии. B>То, что ты привел — и есть правила для вызова обычной функции, если ты этого не заметил ЛБ>>К тому же приведенный здесь пример
явно показывал несоответствие. B>Он показал лишь то, что используемый тобой компилятор выполняет вызов в соответствии с требованием стандарта, и ничего более. ЛБ>>А тут еще один пример
Если ты имеешь ввиду void main(), то эта моя ошибка есть и в первом примере.
Я приношу глубочайшие извинения, переделывал ее из другой функции и оплошал.
Замени, пожалуйста, void на int — все скомпилируется.
ЛБ>>Тут так все перевернуто с ног на голову, что мне остается только улыбнуться: ЛБ>>
B>А у меня есть еще кое-что, кроме улыбок: B>предлагаю помедитировать над следующим примером
Тут нечего медитировать. Специально для тебя продолжение параграфа из стандарта.
Опять же приношу извинения, что первый раз поленился привести полностью.
12.7 (3)
... but not a function, overriding it in a class derived from the constructor
or destructor's class, or overriding it in one of the other base classes
of the most derived object.
Я, пожалуй, остановлюсь на этом, потому как ты говоришь на другом языке. Для
тебя вызов является виртуальным, если он идет через vtbl, а в стандарте просто
нет понятия vtbl. Правила для виртуальных функций описываются так, как в
приведенном параграфе.
Лазар
Re[10]: Почему не вызывается перегруженная виртуальная функц
Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ> ЛБ>Если ты имеешь ввиду void main(), то эта моя ошибка есть и в первом примере.
Нет, я имею ввиду использование using declaration:
7.3.3/14
...
The base class members mentioned by a using-declaration shall be visible
in the scope of at least one of the direct base classes of the class
where the using-declaration is specified.
...
Т.е. нельзя в C писать using A::sf, поскольку она скрыта в непосредственном базовом классе (т.е. в B).
ЛБ>>>Тут так все перевернуто с ног на голову, что мне остается только улыбнуться: ЛБ>>>
B>>А у меня есть еще кое-что, кроме улыбок: B>>предлагаю помедитировать над следующим примером
ЛБ>Тут нечего медитировать. Специально для тебя продолжение параграфа из стандарта. ЛБ>Опять же приношу извинения, что первый раз поленился привести полностью.
ЛБ>12.7 (3) ЛБ>... but not a function, overriding it in a class derived from the constructor ЛБ>or destructor's class, or overriding it in one of the other base classes ЛБ>of the most derived object.
Я читал этот пункт стандарта, но все равно спасибо.
ЛБ>Я, пожалуй, остановлюсь на этом, потому как ты говоришь на другом языке.
Да, похоже, что мы говорим на разных языках.
ЛБ>Для тебя вызов является виртуальным, если он идет через vtbl, а в стандарте просто ЛБ>нет понятия vtbl.
Я такого не говорил — наоборот, я говорил, что вызов виртуальной функции из конструктора может быть реализован через таблицу виртуальных функций (если виртуальный функции реализованы через таблицу).
ЛБ>Правила для виртуальных функций описываются так, как в приведенном параграфе.
С этим трудно спорить, однако эти правила немного отличаются от того, с чего начал ты:
На самом деле работают, более того, работают именно как
виртуальные, просто вызывается последний override на момент
создания объекта класса исполняемого конструктора/деструктора
Поясню свой пример, поскольку, как мне кажется, его смысл от тебя ускользнул:
в деструкторе D1 вызов vg приводит к вызову B::vg, хотя подобъект D2 еще жив, и таким образом последний override — это D2::vg.
Это поведение, естественно, соответствует правилам из 12.7/3, но никак не твоему "правилу последнего override — а".
Любите книгу — источник знаний (с) М.Горький
Re[6]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Bell, Вы писали:
Б>>На самом деле работают, более того, работают именно как виртуальные
ЛБ>>Фразу, что я комментировал можно понимать как отключение виртуального механизма вызова и переход на статический при вызове виртуального метода из конструктора/деструктора.
B>Что же касается моего утверждения о механизмах вызова, то тут я погорячился. B>Стандарт требует, чтобы вызов (как приямой, так и опосредованный) виртуальной функции в конструкторе/деструкторе соответствовал прямому вызову "обычной" функции.
Я не совсем понимаю почему это утверждение используется как аргумент при в пользу некоего "отсутствия виртуальности". Например, одним из применений полиморфизма в С++ является "уточнение" поведения некоторого невиртуального метода базового класса-"скелета алгоритма" путем предоставления конкретных реализаций виртуальных функций это базового класса в классе-наследнике
Эта функциональность будет прекрасно работать и при вызове метода 'Run()' из конструктора/деструктора класса-наследника с той только оговоркой, что виртуальные вызовы буду выполняться так, как будто динамический типом объекта является именно тот тип, чей конструктор/деструктор непосредственно работает в данный момент. Тем не менее вызовы виртуальных методов будут вести себя совершенно "виртуально" (с учетом предыдущей оговорки). Виртуальность никак не отрицается тем фактом, что опосредованный вызов виртуальных функций соответствует по своему эффекту их прямому вызову из конструктора/деструктора класса-наследника.
Изначальное утверждение Лазара было, по моему мнению, совершенно верным. Виртуальность в С++ работает из конструткоров/деструкторов "до уровня" непосредственно конструируемого/деструктируемого объекта. Именно этого требует спецификация языка. А как это реализовано -"прямыми" вызовами или "непрямыми" — это уже детали реализации. Спецификация не оперирует такими понятиями.
Best regards,
Андрей Тарасевич
Re[4]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, ekamaloff, Вы писали:
E>Как видно из этого листинга, ни о каком виртуальном вызове (в случае с Foo), тут речи и не идет. Адрес берется не из vtable, а уже известен на момент компиляции. Поэтому фраза "работают именно как виртуальные", как справедливо заметил Bell, совершенно несправедлива.
Концепция "работать как виртуальные" не обязана иметь какого-то отображенния на машинный код, порождаемый компилятором. На то она и концепция. Спецфикация языка требует, чтобы виртуальные методы, вызываемые (прямо или опосредованно) из конструкторов/деструкторов работали как виртуальные с той только оговоркой, что в качестве кандидатов на вызов рассматриваются только методы, определенные выше в иерархии классов, чем непосредственно конструируемый/деструктируемый класс. Это отличается от "полноценной" виртуальности, но этого в общем случае соврешенно не достаточно для того, чтобы отицать наличие виртуальность полностью.
Да, в случае непосредственного вызова виртуального метода из конструктора/деструктора конкретный целевой метод всегда можно определить на стадии компиляции. Соответсвенно, разумным будет сгенерировать код, который выполняет более эффективный прямой вызов. Точно также, как это можно делать вообще всегда, когда динамический тип объекта известен на стадии компиляции. Например, компиялтор иимете полное право сгенерировать код прямого вазова и в таких случаях:
CClass c;
c.some_virtual_method();
// Вызов виртуального метода. Практически любой компилятор странслирует в прямой вызов
// c.CClass::some_virtual_method();
CClass* pc = &c;
pc->some_virtual_method();
// "Умный" компилятор сообразит, что динамический тип '*pc' есть 'CClass' и генерирует прямой вызов
// pc->CClass::some_virtual_method()
Но на уровне языка вызовы от этого не перестают быть виртуальными. Остальное — детали реализации.
Best regards,
Андрей Тарасевич
Re[7]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Я не совсем понимаю почему это утверждение используется как аргумент при в пользу некоего "отсутствия виртуальности".
... АТ>Эта функциональность будет прекрасно работать и при вызове метода 'Run()' из конструктора/деструктора класса-наследника с той только оговоркой, что виртуальные вызовы буду выполняться так, как будто динамический типом объекта является именно тот тип, чей конструктор/деструктор непосредственно работает в данный момент.
Никто не доказывает "отсутсвие виртуальности". Более того, в одном из своих постов
я прямо говорил об использовании механизма виртуального вызова. Однако я не согласен с тем, что
...
На самом деле работают, более того, работают именно как виртуальные, просто вызывается последний override на момент создания объекта класса исполняемого конструктора/деструктора.
Пример, который я приводил, ИМХО достаточно красноречиво говорит о том, что это объяснение не верно.
Ну а твое объяснение (с учетом немаловожной, ИМХО, "оговорки") объясняет все правильно, впрочем как и обяснение "вызов виртуальной функции в конструкторе/деструкторе осуществляется по тем же правилам, что и прямой вызов "обычной" функции".