В крайних случаях замена #include на декларацию в прямом выражении может без колебаний изменить значение кода.
Не могли бы вы помочь мне найти несколько примеров?
Ответ 1
Вот два случая. Один из них - UB, другой, я думаю, определяется изменением поведения (при отсутствии ODR или подобных нарушений: т.е. Никакой вызов foo никогда не видит определение A, но я не уверен)
замена struct A; на struct A:B{}; изменит, какая из foo перегрузок вызывается.
Кроме того, delete A; будет вызывать ~A(), если его видно, когда вызывается delete A;. В противном случае, если существует нетривиальный деструктор, мы имеем UB. В этом случае смысл кода изменяется в том смысле, что он переходит от UB к DB, что я предполагаю смену смысла.
Ответ 2
Один из самых коварных примеров, о которых я знаю, - это приведение C-стиля в сочетании с наследованием.
Скажите, что у вас есть:
class Parent1 {};
class Parent2 {};
class Child : public Parent1, public Parent2 {};
Затем в каком-то другом файле вы отбрасываете из Parent2 в child:
Parent2* parent2_ptr = new Child;
Child* obj = (Child*)parent2_ptr;
При полном определении C-style cast является static_cast, правильно фиксируя адрес. С объявлением вперед (Child) C-style cast становится reinterpret_cast, незаметно нарушающим код.