Что такое “Правило одного определения” (One definition rule)?

301
22 сентября 2017, 20:14

Что такое "Правило одного определения"?

Answer 1

Правило одного определения (One definition rule или ODR) состоит из нескольких пунктов:

  1. Единица трансляции не может содержать больше одного определения

    • переменной,
    • функции,
    • класса (в т.ч. struct),
    • перечисления,
    • шаблона.
  2. Программа должна содержать ровно одно определение:

    • каждой не-inline функции,
    • каждой ODR-использованной переменной.

    Переменная считается "ODR-использованной" (odr-used), если она находится в потенциально вычисляемом выражении. Здесь есть некоторые исключения, но они не интересны в контексте самого правила одного определения.

    Под "программой" понимается ее исходный код, а также стандартная и пользовательские библиотеки. Компилятор не обязан выдавать какие-либо ошибки или предупреждения, если это правило нарушено.
    Все inline функции должны быть определены в каждой единице трансляции, где они ODR-использованы.

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

  4. Программа может содержать несколько определений

    • класса,
    • перечисления,
    • inline функции с внешним связыванием,
    • шаблона класса,
    • шаблона не-static функции,
    • статического члена данных шаблона класса,
    • метода шаблона класса,
    • неполной специализации шаблона,

    если они находятся в разных единицах трансляции и удовлетворяют следующим условиям:

    • каждое определение должно состоять из одной и той же последовательности токенов;
    • имена, использованные в определении (в т.ч. использованные неявно), должны относиться к одним и тем же сущностям, или быть определены в самом определении;
    • в каждом определении сущности должны иметь одно и то же связывание;
    • если в определении есть вызов функции со значением аргумента по умолчанию, считается что токены значения вставляются в определение, и таким образом подпадают под правила, написанные выше;
    • если определение - это класс с неявно-определенным конструктором, он должен вызывать одни и те же конструкторы своих членов и базовых классов.

    Если это правило соблюдено, программа будет вести себя так, как будто в ней было только одно определение. В противном случае поведение программы не определено.

Примеры

ODR-использованные переменные

// Единица трансляции 1:        |     // Единица трансляции 2:
int x = 1;                      |     int x = 1;
int y = 1;                      |     int y = 1;
decltype(x) xx; // ОК, "x" использована в не-вычисляемом контексте.
int z = y; // нарушение ODR, "y" определена в двух единицах трансляции и ODR-использована.

Нарушение правила 4 (разные конструкторы базового класса)

// Единица трансляции 1:        |     // Единица трансляции 2:
struct X {                      |     struct X {
  X(int);                       |       X(int);
  X(int, int);                  |       X(int, int);
};                              |     };
                                |
X::X(int = 0) { }               |     X::X(int = 0, int = 0) { }
                                |
class D: public X { };          |     class D: public X { };

Неявно определенный конструктор D::D() нарушает ODR - в одном случае он использует X(int), а в другом - X(int, int)

Более простой случай нарушения ODR:

// g.h                        
void f(float t);
inline void g() { f(1); }       
---------------------------------------------------------------------
// f_int.h
void f(int);
--------------------------------+------------------------------------
// main.cpp                     |    // f.cpp
#include "g.h"                  |    #include "f_int.h"
                                |    #include "g.h"
int main() {                    |
  g();                          |    void f(int) {}
}                               |    void f(float t) {}

Функция g вызывает разные функции f в разных единицах трансляции.
Это нарушает пункт "имена ... должны относиться к одним и тем же сущностям".

READ ALSO
Вылет программы C++(Segmentation fault)

Вылет программы C++(Segmentation fault)

После появления запроса на ввод чисел программа вылет с такой ошибкой: Segmentation fault (core dumped)

244
Запуск exe с флеш носителя

Запуск exe с флеш носителя

Добрый день! У меня такая проблема компилирую exe, который необходимо запустить с флеш носителя на другом PCПри запуске, АВ Avast пропускает исполняемый...

247
Заменить все символы на пробелы

Заменить все символы на пробелы

Я считываю строку, состоящую из чисел, разделенных различными символами, как удалить из строки все символы, заменив их на пробелы(хочу посчитать...

324
Алгоритм суммы произведений

Алгоритм суммы произведений

Привет! Помогите пожалуйста с создать алгоритм для суммы произведенийДолжен быть цикл в цикле, первый сделал (произведение), а как теперь...

208