Interested Article - Правило одного определения

Правило одного определения (One Definition Rule, ODR) — один из основных принципов языка программирования C++ . Назначение ODR состоит в том, чтобы в программе не могло появиться два или более конфликтующих между собой определения одной и той же сущности ( типа данных , переменной , функции , объекта , шаблона ). Если это правило соблюдено, программа ведёт себя так, как будто в ней существует только одно, общее определение любой сущности. Нарушение ODR, если оно не будет обнаружено при компиляции и сборке проекта, приводит к непредсказуемому поведению программы .

Причины появления

Синтаксис языка C++ определяет для ряда сущностей необходимость совмещения описания с объявлением, либо дублирование в объявлении описания. Это связано с отсутствием в языке полноценной поддержки модульности , из-за чего на этапе компиляции файла в его тексте (то есть самом тексте компилируемого модуля и текстах всех включённых в него заголовочных файлов ) должны содержаться достаточные для компилятора сведения обо всех используемых программных сущностях, определённых в других единицах трансляции . Так, например, для компиляции файла, в котором используется некий класс, необходимо, по меньшей мере, определение самого класса и его публичных свойств и методов , а для использования в файле встраиваемой ( inline ) функции каждый использующий файл должен включать её полный текст. Ясно, что для обеспечения согласованности программы все такие определения в пределах системы должны быть идентичны. Обычно это обеспечивается тем, что интерфейсная часть единицы трансляции описывается в соответствующем заголовочном файле, который затем подключается везде, где необходимо использовать определённые в ней сущности. Но в действительности ничто не мешает включить в разные компилируемые файлы разные описания одних и тех же элементов программы. Компилятор не сможет обнаружить такую ошибку, так как в момент трансляции он обрабатывает код только одного модуля.

Другая коллизия возможна в случае, когда в программе окажутся два различных определения одного и того же элемента, либо расхождение между внешним объявлением (со спецификатором extern) и фактическим определением того же элемента. Например, если у внешнего объявления функции и её определения окажется различное число параметров , то вызов функции будет некорректным, но компилятор не сможет этого обнаружить, так как в момент работы видит только одну единицу трансляции.

В своей знаменитой книге «Введение в язык C++» Бьёрн Страуструп приводит следующий пример:

 // file1.c:
     int a = 1;
     int b = 1;
     extern int c;
 // file2.c:
     int a;
     extern double b;
     extern int c;

В данном примере 3 ошибки: a определено дважды («int a;» является определением, где начальное значение a не задано), b в описании и определении имеет различные типы, а c описано дважды, но не определено. Эти виды ошибок в C++ не могут быть обнаружены компилятором . В большинстве случаев их обнаруживает компоновщик .

Ограничения, накладываемые на повторные описания и определения программных сущностей в C++, соблюдение которых гарантирует адекватную обработку таких описаний и определений, сведены в описании языка в так называемое «Правило одного определения».

Положения правила

  1. В пределах любой единицы трансляции шаблон , тип данных , функция , или объект не могут иметь более одного определения, хотя могут иметь неограниченное число объявлений. Определение порождает сущность.
  2. В пределах программы (совокупности всех единиц трансляции) внешний объект или внешняя не-inline функция не могут иметь больше одного определения; если объект или функция используются, у каждого из них должно быть строго по единственному определению. Можно объявить объект или функцию, которые не будут использованы в программе, в этом случае не потребуется и их определения. Ни в коем случае не должно быть более одного определения.
  3. Типы, шаблоны и inline-функции (то есть те сущности, у которых определение полностью или частично совмещается с объявлением) могут определяться в более чем одной единице трансляции, но для каждой такой сущности все её определения должны быть идентичны.
  4. Определения объектов и функций, не являющихся внешними, в разных единицах трансляции определяют различные сущности, даже если их имена и типы совпадают. Эти определения беспрепятственно могут различаться.

Некоторые нарушения ODR могут быть найдены компилятором , но большинство из них обнаруживаются только на этапе компоновки.

Дополнительная информация

Источник —

Same as Правило одного определения