Как отметил @Charles Bailey, ваш A::f
фактически используется, даже если он чисто виртуальный. Но это не главное.
Неправильно, что правило единого определения не применяется к функциям, которые не используются. У нас есть:
3.2p1 Ни одна единица перевода не должна содержать более одного определения любой переменной, функции, типа класса, типа перечисления или шаблона.
3.2p3 Каждая программа должна содержать ровно одно определение каждой не встроенной функции или объекта, которая используется в этой программе; Диагностика не требуется.
Вместе эти требования, по-видимому, подразумевают, что используемая функция должна иметь ровно одно определение, а неиспользуемая функция (включая чисто виртуальную функцию, которая никогда не вызывается явным образом) может не иметь ни определения, ни одного определения. В любом случае, множественные определения для не встроенной функции делают программу плохо сформированной.
По крайней мере, я вполне уверен, что это намерение. Но вы можете оказаться в дыре во фразировке, поскольку очень буквальное прочтение нигде не говорит о том, что несколько разных определений одной и той же неиспользуемой функции в разных единицах перевода плохо сформированы.
// x.cpp
void f() {}
void g() {}
// y.cpp
#include <iostream>
void f() {
std::cout << "Huh" << std::endl;
}
void h() {}
// z.cpp
void g();
void h();
int main() {
g();
h();
return 0;
}