Что такое "Правило одного определения" (One definition rule)?

1,00
р.
Что такое "Правило одного определения"?

Ответ
Правило одного определения (One definition rule или ODR) состоит из нескольких пунктов:
Единица трансляции не может содержать больше одного определения
переменной, функции, класса (в т.ч. struct), перечисления, шаблона.
Программа должна содержать ровно одно определение:
каждой не-inline функции, каждой ODR-использованной переменной.
Переменная считается "ODR-использованной" (odr-used), если она находится в потенциально вычисляемом выражении. Здесь есть некоторые исключения, но они не интересны в контексте самого правила одного определения. Под "программой" понимается ее исходный код, а также стандартная и пользовательские библиотеки. Компилятор не обязан выдавать какие-либо ошибки или предупреждения, если это правило нарушено. Все inline функции должны быть определены в каждой единице трансляции, где они ODR-использованы. Единица трансляции должна содержать ровно одно определение класса, если он был использован таким образом, что его тип должен быть полным. Программа может содержать несколько определений
класса, перечисления, inline функции с внешним связыванием, шаблона класса, шаблона не-static функции, статического члена данных шаблона класса, метода шаблона класса, неполной специализации шаблона,
если они находятся в разных единицах трансляции и удовлетворяют следующим условиям:
каждое определение должно состоять из одной и той же последовательности токенов имена, использованные в определении (в т.ч. использованные неявно), должны относиться к одним и тем же сущностям, или быть определены в самом определении в каждом определении сущности должны иметь одно и то же связывание если в определении есть вызов функции со значением аргумента по умолчанию, считается что токены значения вставляются в определение, и таким образом подпадают под правила, написанные выше если определение - это класс с неявно-определенным конструктором, он должен вызывать одни и те же конструкторы своих членов и базовых классов.
Если это правило соблюдено, программа будет вести себя так, как будто в ней было только одно определение. В противном случае поведение программы не определено.

Примеры
ODR-использованные переменные
// Единица трансляции 1: | // Единица трансляции 2: int x = 1 | int x = 1 int y = 1 | int y = 1 decltype(x) xx // ОК, "x" использована в не-вычисляемом контексте. int z = y // нарушение ODR, "y" определена в двух единицах трансляции и ODR-использована.
Нарушение правила 4 (разные конструкторы базового класса)
// Единица трансляции 1: | // Единица трансляции 2: struct X { | struct X { X(int) | X(int) X(int, int) | X(int, int) } | } | X::X(int = 0) { } | X::X(int = 0, int = 0) { } | class D: public X { } | class D: public X { }
Неявно определенный конструктор D::D() нарушает ODR - в одном случае он использует X(int), а в другом - X(int, int)
Более простой случай нарушения ODR:
// g.h void f(float t) inline void g() { f(1) } --------------------------------------------------------------------- // f_int.h void f(int) --------------------------------+------------------------------------ // main.cpp | // f.cpp #include "g.h" | #include "f_int.h" | #include "g.h" int main() { | g() | void f(int) {} } | void f(float t) {}
Функция g вызывает разные функции f в разных единицах трансляции. Это нарушает пункт "имена ... должны относиться к одним и тем же сущностям".