Правила ODR:

  1. В пределах любой единицы трансляции шаблон, тип данных, функция или объект не могут иметь более одного определения, но могут иметь неограниченное число объявлений.
  2. В пределах программы (совокупности всех единиц трансляции) объект или не-inline функция не могут иметь более одного определения; если объект или функция используются, у каждого из них должно быть строго по единственному определению.
  3. Типы, шаблоны и inline-функции (то есть те сущности, определение которых полностью или частично совмещается с их объявлением) могут определяться в более чем одной единице трансляции, но для каждой такой сущности все её определения должны быть идентичны.

ODR можно легально нарушить с помощью ключевого слова inline.

Связывание (linkage)

Без связывания

Символ доступен только из блока, в котором был объявлен.

Внутреннее связывание (internal linkage)

Символ доступен из всех блоков в данной единице трансляции.

Внешнее связывание (external linkage)

Символ доступен из всех блоков в других единицах трансляции.

Модульное связывание (module linkage)

Символ доступен из всех блоков только данного модуля или в других единицах трансляции того же самого именованного модуля.

Символы, определенные в пространстве имен, имеют модульное связывание, если их объявление находится в именованном модуле и не было экспортировано (exported), и если они не имеют внутреннего связывания.

// TODO: не уверен в этом пункте. нужно изучить этот вопрос.

Какие ключевые слова влияют на связывание?

  • Использование static в глобальном пространстве имен дает символу внутреннее связывание.
  • Использование extern дает внешнее связывание.

Компилятор по умолчанию дает символам следующие связывания:

  • Non-const глобальные переменные - внешнее связывание
  • Const глобальные переменные - внутреннее связывание
  • Функции - внешнее связывание
  • Объявление в анонимном пространстве имен - внутреннее связывание.

То есть, например, константы в файлах .h, по умолчанию будут продублированы в каждой единице трансляции при подключении через #include. То есть каждый файл .cpp будет иметь свою собственную константу.

Это может негативно сказаться на скорости компиляции при изменении этой константы, так как перекомпилироваться будут все единицы трансляции, в которых эта константа присутствовала.

Чтобы этого избежать, можно пометить константы в файле .h как extern, затем инициализировать их единожды в каком-нибудь .cpp, затем подключать этот хедер где угодно.

// constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants {
    extern const double pi;
    extern const double avogadro;
}

#endif
// constants.cpp
#include "constants.h"

namespace constants {
    // keyword extern can be omitted
    extern const double pi { 3.14159 };
    extern const double avogadro { 6.0221413e23 };
}

Однако у этого метода есть недостаток. Эти константы теперь могут считаться константами времени компиляции только в файле, в котором они фактически определены (constants.cpp), а не где-либо еще.

Ключевое слово inline

Ключевое слово inline говорит линкеру игнорировать то, что в нескольких .cpp файлах встречаются одинаковые определения. Линкер будет брать рандомное, предполагая, что все они равны. Если они не равны, поведение не определено.

Также слово inline может объявлять inline-переменную и влиять на связывание (linkage) этой переменной начиная со стандарта C++17, если она глобальная, дополняя правила связывания переменных:

  • static - внутреннее связывание
  • extern - внешнее связывание
  • inline - внешнее связывание
  • const && inline - внешнее связывание
  • const && !inline - внутреннее связывание

Предыдущий пример можно переписать следующим образом:

// constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants {
    inline constexpr double pi { 3.14159 };
    inline constexpr double avogadro { 6.0221413e23 };
}

#endif

Также можно неформально сказать, что все шаблонные классы/методы/функции являются inline автоматически.

Constexpr-функции также неявно являются inline.


Заимствования:

Inline variables / Хабр (habr.com)