Одно правило определения - One Definition Rule

В Одно правило определения (ODR) является важным правилом C ++ язык программирования который предписывает, что объекты и не встроенные функции не могут иметь более одного определения во всей программе и шаблоне, а типы не могут иметь более одного определения по единица перевода. Он определен в стандарте ISO C ++ (ISO / IEC 14882 ) 2003, в разделе 3.2.

Резюме

Короче говоря, ODR гласит:

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

Некоторые нарушения ODR должны быть диагностированы компилятор. Другие нарушения, особенно те, которые охватывают единицы перевода, диагностировать не требуется.[1]

Примеры

В общем, единица трансляции должна содержать не более одного определения любого типа класса. В этом примере два определения типа класса C встречаются в одном и том же единица перевода. Обычно это происходит, если заголовочный файл включается дважды одним и тем же исходным файлом без соответствующего защита жатки.

учебный класс C {}; // первое определение Cучебный класс C {}; // ошибка, второе определение C

В дальнейшем формирование указателя на S или определение функции, принимающей ссылку на S, являются примерами допустимых конструкций, поскольку они не требуют, чтобы тип S был полный. Следовательно, определение не требуется.[2]

Определение объекта типа S, функции, принимающей аргумент типа S, или использования S в размер Выражение - это примеры контекстов, в которых S должно быть полным и, следовательно, требовать определения.[2]

структура S;     // объявление SS * п;        // хорошо, определение не требуетсяпустота ж(S&);   // хорошо, определение не требуетсяпустота ж(S*);   // хорошо, определение не требуется S ж();        // хорошо, определение не требуется - это только объявление функции!S s;          // ошибка, требуется определениеразмер(S);    // ошибка, требуется определение

Более одного определения

В некоторых случаях может быть более одного определения типа или шаблона. Программа, состоящая из нескольких файлов заголовков и исходных файлов, обычно будет иметь более одного определения типа, но не более одного определения на единицу перевода.

Если программа содержит более одного определения типа, каждое определение должно быть эквивалентным.[3]

Определения статических константных членов данных

В предварительном стандарте C ++ все статические элементы данных требовали определения вне их класса. Однако в процессе стандартизации C ++ было решено отменить это требование для статических неотъемлемых членов. Намерение состояло в том, чтобы разрешить использование, например:

структура C {  статический const int N = 10;};char данные[C::N]; // N "использовано" без внеклассного определения

без пространство имен определение объема для N.

Тем не менее, формулировка стандарта C ++ 1998 г. по-прежнему требовала определения того, использовался ли член в программе.[4] Это включало член, появляющийся где угодно, кроме как операнда для размер или же типичный, что фактически делает приведенное выше неверным.[5]

Это было идентифицировано как дефект, и формулировка была изменена таким образом, чтобы такой член мог появиться где угодно постоянное выражение требуется, не требуя внеклассного определения. Это включает в себя множество границы case выражения, инициализаторы статических членов и аргументы шаблона, не относящиеся к типу.[6]

структура C {  статический const int N = 10;  статический const int U = N; // Допустимо для C ++ 03};char данные[C::N]; // Допустимо для C ++ 03шаблон<int> структура D;шаблон<> структура D<C::N> {}; // Допустимо для C ++ 03

Однако использование интегрального члена static const везде, кроме случаев, когда требуется интегральное выражение-константа, требует определения:[7]

структура C {  статический const int N = 10;};int главный() {  int я = C::N; // Неправильно сформирован в C ++ 03. Требуется определение C :: N.}

Это требование было смягчено в более позднем стандарте, C ++ 11.[7]

Пример, показывающий неожиданные побочные эффекты

Нам нужно 4 файла: «odr.h», «main.cpp», «odr1.cpp», «odr2.cpp»

Аббревиатура «odr» здесь является сокращением от «правила одного определения».

odr.h:

// абстрактный базовый классучебный класс CBase {общественный:	виртуальный пустота ххх() = 0;	виртуальный ~CBase() = дефолт;};внешний CBase *odr1_create();внешний CBase *odr2_create();

main.cpp

#включают "odr.h"int главный(int argc, char **argv){	CBase *o1 = odr1_create();	CBase *o2 = odr2_create();	o1->ххх();	o2->ххх();}

odr1.cpp

#включают <stdio.h>#включают "odr.h"учебный класс CDummy : общественный CBase {общественный:	пустота ххх() отменять {		printf("odr ONE dummy: Привет п");	}};CBase *odr1_create() {	возвращаться новый CDummy();}

odr2.cpp

#включают <stdio.h>#включают "odr.h"учебный класс CDummy : общественный CBase {общественный:	пустота ххх() отменять {		printf("odr TWO dummy: World п");	}};CBase *odr2_create() {	возвращаться новый CDummy();}

Под оболочкой Linux, чтобы попробовать, скомпилируйте с помощью:

g ++ -c odr1.cppg ++ -c odr2.cppg ++ -c main.cppg ++ -o odr main.o odr1.o odr2.o

В Windows Visual Studio «Командная строка инструментов сборки» выполните компиляцию с помощью:

cl / c main.cppcl / c odr1.cppcl / c odr2.cppcl /Feodr.exe main.obj odr1.obj odr2.obj

При исполнении ожидал вывод:

odr ONE dummy: Helloodr TWO dummy: World

Но вы, скорее всего, получите:

odr ONE манекен: Helloodr ONE манекен: Привет

Проблема в том, что компоновщик C ++ должен выяснить, как построить таблицу виртуальных методов для (двух разных) классов «CDummy», и это работает, только если имена классов разные.

Смотрите также

Рекомендации

  1. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 3
  2. ^ а б ISO /IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 4
  3. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 5
  4. ^ ISO /IEC (1998). ISO / IEC 14882: 1998 (E): Языки программирования - C ++ §9.4.2 Статические элементы данных [class.static.data] пункт 4
  5. ^ ISO /IEC (1998). ISO / IEC 14882: 1998 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 2
  6. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §5.19 Постоянные выражения [expr.const] пункт 1
  7. ^ а б «Когда требуется определение статического элемента данных?». WG21. Получено 2009-04-15.