Что такое одно правило определения в C++?
Что именно говорит одно определение в C++? Единственный надежный случай, который я могу найти, это язык программирования C++, 3-й. изд., с. 9.2.3. Есть ли официальное определение правила, кроме этого?
2 ответа
Правда в стандарте (3.2 Одно определение правила):
Ни одна единица перевода не должна содержать более одного определения любой переменной, функции, типа класса, типа перечисления или шаблона.
[...]
Каждая программа должна содержать ровно одно определение каждой не встроенной функции или объекта, которая используется в этой программе; Диагностика не требуется. Определение может явным образом появиться в программе, оно может быть найдено в стандартной или пользовательской библиотеке или (при необходимости) неявно определено (см. 12.1, 12.4 и 12.8). Встроенная функция должна быть определена в каждой единице перевода, в которой она используется.
«Правило одного определения» — это подраздел [basic.def.odr] стандарта C++. Этот подпункт содержит ряд правил. Я суммирую самые важные из них.
Во-первых, вы не можете определять что-то несколько раз в одном файле. Если вы определяете что-то в нескольких файлах, определения должны быть идентичными. Более того, если вы что-то используете, у этого должно существовать определение.
В разделах ниже это описано более подробно.
Только одно определение на единицу перевода [1]
Первое и самое основное правило — [basic.def.odr] p2:
Ни одна единица перевода не должна содержать более одного определения любого определяемого элемента .
Например, это означает, что вы не можете написать следующее:
void foo() {} // OK
void foo() {} // error, more than one definition of foo in the same TU
На практике компилятор выдаст ошибку, сообщающую вам «переопределение foo».
[1] Единица перевода — это, по сути, один исходный файл C++. Заголовок не является единицей перевода, он входит в состав единиц перевода.
Только одно определение для каждой программы, за исключением шаблонов иinline
Более того, [basic.def.odr] p14 регулирует то, что происходит между несколькими единицами перевода:
Для любого определяемого элемента с определениями в нескольких единицах перевода:
- если
D
является невстроенной нешаблонной функцией или переменной, или- если определения в разных единицах перевода не удовлетворяют следующим требованиям,
программа плохо сформирована; [...]
Например, это означает, что у вас не может быть двух определений.void foo() {}
в разных исходных файлах. На практике это приведет к ошибке компоновщика, сообщающей вам о «несколько определениях foo».
Шаблоны и встроенные функции являются особенными, поскольку их можно определить в нескольких единицах перевода, если эти определения соответствуют строгим правилам. По сути, определения должны быть эквивалентными. Например:
// a.cpp
inline int bar() { return 0; }
// b.cpp
inline int bar() { return 0; } // OK
// c.cpp
inline int bar() { return 1; } // ill-formed, no diagnostic required
Обратите внимание, что это правило очень важно для заголовков.#include
по существу копирует/вставляет код в единицы перевода, поэтому определение чего-либо в заголовке рискует нарушить это правило.
Все, что используется odr [2], должно быть определено
В-третьих, он также определяет так называемое . Интуитивно, означает, что вы используете некоторую конструкцию таким образом, чтобы ее определение существовало. Например:
int foo(); // declaration, not a definition
using foo_type = decltype(foo); // decltype (unevaluated operand) is not odr-use.
int x = foo(); // calling a function is odr-use
Использование чего имеет последствия, указанные в [basic.def.odr] стр.11:
Каждая программа должна содержать по крайней мере одно определение каждой функции или переменной, которая odr-useиспользуется-либо odrиспользование odr в этой программе за пределами отброшенного оператора ; не требуется диагностика.
На практике, если вы используете функцию, и она нигде не определена, вы получаете ошибку компоновщика, сообщающую: «нет определения foo».
[2] Первоначально этот термин назывался просто «использование»; В C++11 введен термин «odr-use», чтобы сделать его более формальным.