One Definition Rule (ODR) — это фундаментальное правило C++, которое требует, чтобы в пределах всей программы (всех translation units) для каждого объекта, функции или класса была ровно одна определённая реализация (definition).
ODR нарушается, если:
Это приводит к трудноуловимым, непредсказуемым багам, некорректной линковке или, что хуже, к разному поведению программы в зависимости от того, как её скомпоновали.
Почему нарушается:
В больших проектах часто копируют/модифицируют .h-файлы без контроля версий, или отделяют код на множество модулей с раздельной компиляцией. Если кто-то меняет inline-функцию только в одном месте, остальные исходные файлы могут иметь старую версию.
Как избежать:
constexpr или, с C++17, inline variables.Можно ли определять static-функции (static void foo()) с одинаковыми именами и различной реализацией в разных .cpp-файлах без последствий?
Многие считают, что static-функции вообще никак не влияют друг на друга между модулями. Ответ: да, можно, потому что каждая из них имеет внутреннее linkage (видима только внутри своего translation unit). Однако для inline-функций и шаблонов такое не гарантируется, что часто путают.
Пример:
// file1.cpp static void foo() { std::cout << "A"; } // file2.cpp static void foo() { std::cout << "B"; }
Вызовы в этих модулях будут независимы.
История
В крупном проекте один разработчик изменил тело inline-функции только в одном из клонов .h-файла. После сборки некоторое поведение стало непредсказуемым: часть модулей работала по старому алгоритму, часть — по новому. Причина — нарушение ODR для inline-функции.
История
При миграции на C++17 переменная-константа определялась в нескольких заголовочных файлах без использования ключевого слова inline. На линковке появилось множество ошибок duplicate symbol. Исправили, объявив
inline constпеременной.
История
Поздно выявили, что сгенерированный .h-файл системы сборки содержал две реализации одного и того же метода шаблонного класса, различавшиеся одной проверкой ifdef в разных сборках. Позже приложение периодически падало из-за расхождения в ABI между связанными модулями.