Понимание этой проблемы - в основном понимание того, как работают модули компиляции C ++. Вещи в заголовочных файлах в основном вставляются в исходный код целого ряда модулей компиляции с помощью операторов #include
. Каждый модуль компиляции компилируется в объектный файл, объектные файлы связываются, и вы получаете конфликты из-за того, что этот материал реплицируется повсеместно.
Исключениями являются вещи, которые (по крайней мере, исторически) не попадают в объектный файл, потому что компилятор работает с ними напрямую (например, встроенные функции), и вещи, которые не могут быть скомпилированы в одном модуле, а затем связаны с другим потому что они не полностью определены (шаблоны). Функции шаблонов часто создаются идентично в нескольких блоках компиляции, а у компоновщика есть специальные умения для удаления дубликатов.
Это означает, что разделение интерфейса и реализации на файлы заголовка и тела не очень чистое. Ада имеет более чистое разделение - но более сложный процесс сборки, чтобы компенсировать IIRC. Java просто отбросила отдельные файлы для интерфейса и реализации.
За прошедшие годы линкеры стали намного более изощренными и взяли на себя часть работы компилятора, и многие из этих объяснений в наши дни просто неверны, но основные паттерны остаются. Шаблонные функции и встроенные функции могут (и часто должны) идти в заголовке вместе со всеми вашими общими объявлениями «не генерировать напрямую объектный код». Обычные определения функций не должны быть в заголовочном файле.