Что такое "Правило одного определения" (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 в разных единицах трансляции. Это нарушает пункт "имена ... должны относиться к одним и тем же сущностям".