Jump to content

Одно правило определения

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

Краткое содержание

[ редактировать ]

Короче говоря, в ODR говорится следующее:

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

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

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

class C {}; // first definition of C
class C {}; // error, second definition of C

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

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

struct S;     // declaration of S
S * p;        // ok, no definition required
void f(S&);   // ok, no definition required
void f(S*);   // ok, no definition required 
S f();        // ok, no definition required - this is a function declaration only!

S s;          // error, definition required
sizeof(S);    // error, definition required

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

[ редактировать ]

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

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

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

[ редактировать ]

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

struct C {
  static const int N = 10;
};
char data[C::N]; // N "used" without out-of-class definition

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

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

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

struct C {
  static const int N = 10;
  static const int U = N; // Legal per C++03
};

char data[C::N]; // Legal per C++03

template<int> struct D;

template<> struct D<C::N> {}; // Legal per C++03

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

struct C {
  static const int N = 10;
};

int main() {
  int i = C::N; // Ill-formed in C++03. Definition of C::N required.
}

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

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

[ редактировать ]

Нам понадобится 4 файла: "odr.h", "main.cpp", "odr1.cpp", "odr2.cpp"

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

одр.х:

// abstract base class
class CBase {
public:
	virtual void xxx() = 0;
	virtual ~CBase() = default;
};

extern CBase *odr1_create();
extern CBase *odr2_create();

main.cpp

#include "odr.h"

int main(int argc, char **argv)
{
	CBase *o1 = odr1_create();
	CBase *o2 = odr2_create();
	o1->xxx();
	o2->xxx();
}

odr1.cpp

#include <stdio.h>
#include "odr.h"

class CDummy : public CBase {
public:
	void xxx() override {
		printf("odr ONE dummy: Hello\n");
	}
};

CBase *odr1_create() {
	return new CDummy();
}

odr2.cpp

#include <stdio.h>
#include "odr.h"

class CDummy : public CBase {
public:
	void xxx() override {
		printf("odr TWO dummy: World\n");
	}
};

CBase *odr2_create() {
	return new 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», и это работает только в том случае, если имена классов различаются.

См. также

[ редактировать ]
  1. ^ ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §3.2 Одно правило определения [basic.def.odr], параграф. 3
  2. ^ Перейти обратно: а б ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §3.2 Одно правило определения [basic.def.odr], параграф. 4
  3. ^ ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §3.2 Одно правило определения [basic.def.odr], параграф. 5
  4. ^ ИСО / МЭК (1998). ISO/IEC 14882:1998(E): Языки программирования – C++ §9.4.2 Статические элементы данных [class.static.data], параграф. 4
  5. ^ ИСО / МЭК (1998). ISO/IEC 14882:1998(E): Языки программирования – C++ §3.2 Одно правило определения [basic.def.odr], параграф. 2
  6. ^ ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §5.19 Константные выражения [expr.const], параграф. 1
  7. ^ Перейти обратно: а б «Когда требуется определение статического элемента данных?» . РГ21 . Проверено 15 апреля 2009 г.
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 205eaeaf9eba8c6871891064796d736b__1692112140
URL1:https://arc.ask3.ru/arc/aa/20/6b/205eaeaf9eba8c6871891064796d736b.html
Заголовок, (Title) документа по адресу, URL1:
One Definition Rule - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)