Соблюдайте правило одного определения
Правило одного определения (One Definition Rule, ODR) обязывает избегать использования в программе более одного определения одной и той же сущности. Нарушение ODR приводит к неопределенному поведению.
В пределах единицы трансляции допустимо только одно определение:
- переменной;
- функции;
- класса (в т.ч. struct);
- перечисления;
- концепта;
- шаблона.
В пределах программы допустимо только одно определение:
- не-inline функции
- ODR-использованной переменной
Переменная считается "ODR-использованной" (odr-used), если она находится в потенциально вычисляемом выражении.
Все inline функции должны быть определены в каждой единице трансляции, где они ODR-использованы.
Это неполное описание ODR, есть еще ряд нюансов и исключений, которые здесь обсуждаться не будут.
Неправильно:
// utils.h
int add(int a, int b) {
return a + b;
}
// a.cpp
#include "utils.h"
// using add()
// b.cpp
#include "utils.h"
// using add()
Этот код нарушает ODR и приводит к ошибке компиляции: multiple definition of `add(int, int)'
Можно использовать для решения проблемы ключевое слово static, которое позволит создать в каждой единице трансляции свою копию функции, но это раздувает код.
Неправильно:
Другой способ - использование встраиваемой функции.
Правильно:
inline позволяет создать несколько определений. Компоновщик при этом выберет только одно.
До С++17 определение глобальных переменных в заголовочном файле было невозможно.
Пример:
В С++17 появились inline-переменные, которые повторяют логику inline-функций, и решают проблему ODR для данных.
Пример:
Ключевое слово inline для переменной говорит компоновщику: «Эта переменная может быть определена в нескольких единицах трансляции, выбери одну, они все идентичны».