Соблюдайте правило одного определения

Правило одного определения (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, которое позволит создать в каждой единице трансляции свою копию функции, но это раздувает код.

Неправильно:

// utils.h

static int add(int a, int b) {
    return a + b;
}

Другой способ - использование встраиваемой функции.

Правильно:

// utils.h

inline int add(int a, int b) {
    return a + b;
}

inline позволяет создать несколько определений. Компоновщик при этом выберет только одно.

До С++17 определение глобальных переменных в заголовочном файле было невозможно.

Пример:

// utils.h

extern const double pi;

// utils.cpp
#include "utils.h"

const double pi = 3.14159;

В С++17 появились inline-переменные, которые повторяют логику inline-функций, и решают проблему ODR для данных.

Пример:

// utils.h

inline const double pi = 3.14159;

// utils.cpp
#include "utils.h"

// using pi

Ключевое слово inline для переменной говорит компоновщику: «Эта переменная может быть определена в нескольких единицах трансляции, выбери одну, они все идентичны».