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

редактировать
Правило языка программирования C ++

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

Содержание
  • 1 Сводка
  • 2 Примеры
  • 3 Более одного определения
  • 4 Определения статических константных элементов данных
  • 5 Пример, демонстрирующий неожиданные побочные эффекты
  • 6 См. Также
  • 7 Ссылки
Резюме

Вкратце, ODR заявляет, что:

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

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

Примеры

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

class C {}; // первое определение C class C {}; // ошибка, второе определение C

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

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

struct S; // объявление S S * p; // хорошо, определение не требуется void f (S ); // хорошо, определение не требуется void f (S *); // хорошо, определение не требуется S f (); // хорошо, определение не требуется - это только объявление функции! S s; // ошибка, требуется определение sizeof (S); // ошибка, требуется определение
Более одного определения

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

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

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

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

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

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

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

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

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

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

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

Это требование было ослаблено в более позднем стандарте, C ++ 11.

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

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

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

odr.h:

// абстрактный базовый класс class CBase {public: virtual void xxx () = 0; виртуальный ~ CBase () = по умолчанию; }; внешний CBase * odr1_create (); внешний CBase * odr2_create ();

main.cpp

#include "odr.h" int main (int argc, char ** argv) {CBase * o1 = odr1_create (); CBase * o2 = odr2_create (); o1->ххх (); o2->ххх (); }

odr1.cpp

#include #include "odr.h" class CDummy: public CBase {public: void xxx () override {printf ("odr ONE dummy: Hello \ n"); }}; CBase * odr1_create () {вернуть новый CDummy (); }

odr2.cpp

#include #include "odr.h" class CDummy: public CBase {public: void xxx () override {printf ("odr TWO dummy: World \ n"); }}; CBase * odr2_create () {вернуть новый CDummy (); }

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

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

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

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

При выполнении ожидаемый результат :

odr ONE dummy: Hello odr TWO dummy: World

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

odr ONE dummy: Hello odr ONE dummy: Hello

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

См. Также
Ссылки
Последняя правка сделана 2021-06-01 11:47:30
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте