Защищенные функции-члены могут вызываться только внутри базового класса или в его производном классе. Вы не можете звонить им за пределами своего класса. Внешний вызов означает вызов функции-члена переменной с типом класса.
Так
virtual f1(B*b) { return f2(b); }
нормально, потому что f2 работает с самим классом. (вызывается изнутри)
Но
virtual f2(B*b) { return b->f2(this); }
не скомпилируется, потому что f2 работает с b, а не с самим классом D. (вызывается снаружи) Это незаконно.
Чтобы исправить это, B :: f2 должен быть публичным.