Полагаю, реальный вопрос в том, «почему вы хотите?». Иногда возникает ситуация, когда вы включаете заголовочный файл несколько раз в одну и ту же единицу перевода (файл .cpp). Если это так, вы должны использовать include guard , чтобы компилятор был доволен.
Другая причина, по которой это может вызывать у вас проблемы, заключается в том, что вы используете стороннюю библиотеку, которая определяет классы, имена которых конфликтуют с вашими собственными классами. В этом случае вы должны рассмотреть использование пространств имен для устранения неоднозначности.
В обоих случаях 'extern int i;' ссылается на один и тот же объект (объявленный в другом месте), поэтому множественные объявления однозначны. Если вы написали:
extern int i;
extern float i;
Компилятор будет жаловаться на неоднозначность (потому что он не будет знать, какой переменной вы собираетесь манипулировать, если вы написали 'i = 0;'.
Повторяющиеся объявления классов дают возможность того, что объявления отличаются; Опять же, как компилятор узнает, какой из них использовать, когда он встречает 'Foo;'? Я предполагаю, что компилятор мог бы сравнить объявления классов и убедиться, что они на самом деле идентичны, но это будет очень много усилий, когда альтернативные решения (пространства имен, включают в себя охранники, переименование) намного проще (и, вероятно, меньше). сбивает с толку тех, кто в конечном итоге читает код).