Информация об изменениях

Сообщение Re[25]: Книжка по UB от 14.08.2025 10:17

Изменено 14.08.2025 10:18 rg45

Re[25]: Книжка по UB
Здравствуйте, so5team, Вы писали:

S>
S>struct s1 {
S>  virtual void f() { std::cout << "s1::f" << std::endl; }

S>  s1() {
    this->>f(); // (1)
S>  }

S>  void call_f() {
    this->>f(); // (3)
S>  }
S>};

S>struct s2 : public s1 {
S>  void f() override { std::cout << "s2::f" << std::endl; }
S>};

S>int main() {
S>  s2 s; // (2)

S>  s.call_f(); // (4)
S>}
S>


S>В точке (2) у нас конструируется объект типа s2, у которого должен быть собственный переопределенный f.


S>В точке (1) у нас на руках указатель на базовый тип s1 (это this через который идет вызов f).

S>Но в точке (1) вызов виртуального метода через указатель на базовый тип мы получаем не вызов s2::f, а вызов s1::f.

S>В точке (4) у нас все тот же объект типа s2.

S>Через вызов call_f мы приходим в точку (3), в которой у нас на руках опять указатель на базовый тип s1.
S>Однако, в точке (3) вызов виртуального метода через указатель на базовый тип мы получаем вызов s2::f, а не s1::f.

В точке (3) компилятор не знает, какой будет метод вызван и вынужден использовать позднее связывание. А в точке (1) компилятор уже во время компиляции знает, что здесь может быть вызван только s1::f, без вариантов. Да, он может сделать этот вызов через механизм позднего связывания. Только зачем?
Re[25]: Книжка по UB
Здравствуйте, so5team, Вы писали:

S>
S>struct s1 {
S>  virtual void f() { std::cout << "s1::f" << std::endl; }

S>  s1() {
    this->>f(); // (1)
S>  }

S>  void call_f() {
    this->>f(); // (3)
S>  }
S>};

S>struct s2 : public s1 {
S>  void f() override { std::cout << "s2::f" << std::endl; }
S>};

S>int main() {
S>  s2 s; // (2)

S>  s.call_f(); // (4)
S>}
S>


S>В точке (2) у нас конструируется объект типа s2, у которого должен быть собственный переопределенный f.


S>В точке (1) у нас на руках указатель на базовый тип s1 (это this через который идет вызов f).

S>Но в точке (1) вызов виртуального метода через указатель на базовый тип мы получаем не вызов s2::f, а вызов s1::f.

S>В точке (4) у нас все тот же объект типа s2.

S>Через вызов call_f мы приходим в точку (3), в которой у нас на руках опять указатель на базовый тип s1.
S>Однако, в точке (3) вызов виртуального метода через указатель на базовый тип мы получаем вызов s2::f, а не s1::f.

В точке (3) компилятор не знает, какой будет метод вызван и вынужден использовать позднее связывание. А в точке (1) компилятор уже во время компиляции знает, что здесь может быть вызван только s1::f, без вариантов. Да, он может сделать этот вызов через механизм позднего связывания. Только зачем? Где тот пункт стандарта, который обязывает его сделать только так?