WikiDer > Одно правило определения
В Одно правило определения (ODR) является важным правилом C ++ язык программирования который предписывает, что объекты и не встроенные функции не могут иметь более одного определения во всей программе и шаблоне, а типы не могут иметь более одного определения по единица перевода. Он определен в стандарте ISO C ++ (ISO / IEC 14882) 2003, в разделе 3.2.
Резюме
Короче говоря, ODR гласит:
- В любой единице перевода шаблон, тип, функция, или же объект может иметь не более одного определения. Некоторые из них могут иметь любое количество объявлений. Определение предоставляет экземпляр.
- В целом программа, объект или не-встроенная функция не может иметь более одного определения; если используется объект или функция, у них должно быть только одно определение. Вы можете объявить объект или функцию, которые никогда не используются, и в этом случае вам не нужно предоставлять определение. Ни в коем случае не может быть более одного определения.
- Некоторые вещи, например типы, шаблоны и внешний встроенные функции могут быть определены в нескольких единицах перевода. Для данного объекта каждое определение должно иметь одинаковую последовательность жетоны. Не-внешние объекты и функции в разных единицах перевода являются разными сущностями, даже если их имена и типы одинаковы.
Некоторые нарушения 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», и это работает, только если имена классов разные.
Смотрите также
Рекомендации
- ^ ISO/IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 3
- ^ а б ISO/IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 4
- ^ ISO/IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 5
- ^ ISO/IEC (1998). ISO / IEC 14882: 1998 (E): Языки программирования - C ++ §9.4.2 Статические элементы данных [class.static.data] пункт 4
- ^ ISO/IEC (1998). ISO / IEC 14882: 1998 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 2
- ^ ISO/IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §5.19 Постоянные выражения [expr.const] пункт 1
- ^ а б «Когда требуется определение статического элемента данных?». WG21. Получено 2009-04-15.