Проблема в том, что вы определяете объекты с внешней связью в заголовочном файле. Ожидается, что если вы включите этот заголовочный файл в несколько блоков перевода, вы получите несколько определений одного и того же объекта с внешней связью, что является ошибкой.
Правильный способ сделать это зависит от ваших намерений.
Вы можете поместить свои определения в заголовочный файл, но убедитесь, что они имеют внутреннюю связь.
В C это потребовало бы явного static
static const double PI = 3.1415926535;
static const double PI_under_180 = 180.0f / PI;
static const double PI_over_180 = PI/180.0f;
В C ++ static
необязательно (поскольку в C ++ const
объекты имеют внутреннюю связь по умолчанию)
const double PI = 3.1415926535;
const double PI_under_180 = 180.0f / PI;
const double PI_over_180 = PI/180.0f;
Или вы можете поместить неопределяющие объявления в заголовочный файл и поместить определения в один (и только один) файл реализации
Объявления в файле header должны содержать явные extern
и без инициализатора
extern const double PI;
extern const double PI_under_180;
extern const double PI_over_180;
и определения в одном файле реализации должны выглядеть следующим образом
const double PI = 3.1415926535;
const double PI_under_180 = 180.0f / PI;
const double PI_over_180 = PI/180.0f;
(явное extern
в определениях является необязательным, если вышеуказанные объявления предшествуют определениям в той же единице перевода).
Какой метод вы выберете, зависит от ваших намерений.
Первый метод облегчает компилятору оптимизацию кода, поскольку он может видеть фактическое значение константы в каждой единице перевода. Но в то же время концептуально вы получаете отдельные, независимые постоянные объекты в каждой единице перевода. Например, &PI
будет оцениваться по другому адресу в каждой единице перевода.
Второй метод создает действительно глобальные константы, то есть уникальные константные объекты, которые совместно используются всей программой. Например, &PI
будет оцениваться по одному и тому же адресу в каждой единице перевода. Но в этом случае компилятор может видеть фактические значения только в одной и только одной единице перевода, что может помешать оптимизации.
Начиная с C ++ 17, вы получаете третий вариант, который объединяет «лучшее из обоих миров»: встроенные переменные . Встроенные переменные могут быть безопасно определены в заголовочных файлах, несмотря на наличие внешней связи
inline extern const double PI = 3.1415926535;
inline extern const double PI_under_180 = 180.0f / PI;
inline extern const double PI_over_180 = PI/180.0f;
В этом случае вы получаете именованный константный объект, значение инициализатора которого видно во всех единицах перевода. И в то же время объект имеет внешнюю связь, т. Е. Имеет глобальную идентификацию адреса (&PI
одинаково во всех единицах перевода).
Конечно, что-то подобное может понадобиться только для некоторых экзотических целей (большинство сценариев использования в C ++ требуют первого варианта), но функция есть.