Причина, по которой это не по умолчанию, является в основном исторической в наши дни - когда язык C был формализован, #include
было указано, что он должен действовать точно так, как если бы пользователь скопировал и вставил содержимое указанного файла в расположение #include
-линии; и C ++ хотел (и хочет) оставаться максимально совместимым с C, поэтому C ++ унаследовал это поведение от C.
Что касается варианта использования, где включение одного и того же заголовочного файла более одного раза может быть полезным; один случай, где я нашел это полезным, был для моделирования класса шаблонного контейнера в C (потому что C не поддерживает шаблоны непосредственно). У меня был файл-контейнера-реализации-заголовка, который выглядел примерно так (но более детально; здесь я показываю упрощенную версию для удобства чтения):
// MyContainerImplemention.h
// be sure to #define MYTYPE and MYARRAYSIZE
// before #include-ing this file!
struct ArrayOf##MYTYPE
{
MYTYPE arrayOfItems[MYARRAYSIZE];
};
inline void Set##MYTYPE##Item(struct ArrayOf##MyType * container, int which, MYTYPE item)
{
container[which] = item;
}
[... and so on for various other MYTYPE-specific methods ...]
... тогда мои .c файлы могут делать что-то вроде:
#define MYTYPE int
#define MYARRAYSIZE 10
#include "MyContainerImplementation.h"
#undef MYARRAYSIZE
#undef MYTYPE
#define MYTYPE short
#define MYARRAYSIZE 15
#include "MyContainerImplementation.h"
#undef MYARRAYSIZE
#undef MYTYPE
struct ArrayOfint myInts;
struct ArrayOfshort myShorts;
SetintItem(&myInts, 5, 12);
SetshortItem(&myShorts, 3, 2);
[...]
... и в конечном итоге с контейнером «класс» и связанными с ним методами, реализованными для каждого типа данных, без необходимости каждый раз вручную писать новую реализацию контейнера «класс».
Да, это было очень уродливо - но не так уродливо, как если бы пришлось вручную выписывать тысячи строк избыточного кода контейнера. (Реальный файл-контейнера-реализации-заголовка реализовал хеш-таблицу и имел длину в несколько сотен строк)