Почему это не вызов чистой виртуальной функции?

Я попытался "восстановить" пример в этом ответе, чтобы продемонстрировать, как можно вызывать чистую виртуальную функцию.

#include <iostream>
using namespace std;

class A
{
    int id;
public:
    A(int i): id(i) {}
    int callFoo() { return foo(); }
    virtual int foo() = 0;
};

class B: public A
{
public:
    B(): A(callFoo()) {}
    int foo() { return 3; }
};

int main() {
    B b; // <-- this should call a pure virtual function
    cout << b.callFoo() << endl;
    return 0;
}

Но я не получаю ошибку времени выполнения здесь (с С++ 4.9.2), но вывод 3. Я пробовал то же самое с Borland С++ 5.6.4, но там я получаю нарушение доступа. Я думаю, что foo() должен быть чистым виртуальным в вызове конструктора базового класса.

Кто не прав? Должен ли я попробовать больше компиляторов? Я прав в своем понимании виртуальных функций?

Ответ 1

В вашем коде Undefined Поведение: UB вызывает функцию-член на объекте (даже не виртуальном), прежде чем все базовые классы будут инициализированы. С++ 14 (n4140) 12.6.2/14, акцент мой:

Функции-члены (включая функции виртуальных членов, 10.3) могут быть вызваны для строящегося объекта. Аналогично, строящийся объект может быть операндом оператора typeid (5.2.8) или dynamic_cast (5.2.7). Однако, если эти операции выполняются в ctor-инициализаторе (или в функции, называемой непосредственно или косвенно из ctor-инициализатора) до того, как все mem-инициализаторы для базовых классов завершились, результат операции undefined....

ctor-initializer - это весь список, следующий за :. mem-initializer является одним из элементов этого списка.

Ответ 2

Оператор B b; вызывает конструктор по умолчанию B.

При построении B ничего не относящееся к B построено до тех пор, пока A не будет полностью построено.

Поэтому при попытке вызвать callFoo() поведение undefined, так как вы не можете полагаться на настройку v-таблицы для класса B.

Вкратце: поведение вызова чистой виртуальной функции при построении абстрактного класса undefined.