Подтвердить что ты не робот

Если я не использую переменную odr-use, могу ли я иметь несколько определений ее в единицах перевода?

Стандарт, по-видимому, подразумевает, что нет ограничения на число определений переменной, если оно не используется в odr (§3.2/3):

Каждая программа должна содержать ровно одно определение каждой не-встроенной функции или переменной, которая является odr-используемой в этой программе; не требуется диагностика.

Он говорит, что любая переменная не может быть определена несколько раз в пределах единицы перевода (§3.2/1):

Никакая единица перевода не должна содержать более одного определения любой переменной, функции, типа класса, типа перечисления или шаблона.

Но я не могу найти ограничение для не-odr-используемых переменных во всей программе. Так почему я не могу скомпилировать что-то вроде следующего:

// other.cpp
int x;

// main.cpp
int x;
int main() {}

Компиляция и связывание этих файлов с g++ 4.6.3, я получаю ошибку компоновщика для multiple definition of 'x'. Честно говоря, я ожидаю этого, но поскольку x не используется нигде (насколько я могу судить), я не вижу, как стандарт ограничивает это. Или это поведение undefined?

4b9b3361

Ответ 1

Ваша программа нарушает правила привязки. С++ 11 §3.5 [basic.link]/9:

Два имени, которые являются одинаковыми и объявляются в разных областях, должны обозначать один и тот же переменная, функция, тип, перечислитель, шаблон или пространство имен, если

  • оба имени имеют внешнюю связь, либо оба имени имеют внутреннюю связь и объявляются в одной и той же единицы перевода; и

  • оба имени относятся к членам одного и того же пространства имен или к членам, а не по наследованию того же класса; и

  • когда оба имени обозначают функции, параметры-типы-списки функций идентичны; и

  • когда оба имени обозначают функциональные шаблоны, подписи одинаковы.

(Я привел полный абзац, для справки. Вторые две пули здесь не применяются.)

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

Эти два имени не обозначают одну и ту же переменную. Объявление int x; определяет переменную. Поскольку в программе есть два таких определения, в программе есть две переменные. Имя "x" в одной единице перевода обозначает одну из этих переменных; имя "x" в другой единицы перевода обозначает другую. Поэтому программа плохо сформирована.

Ответ 2

Вы правы, что стандарт виноват в этом отношении. У меня такое чувство, что этот случай попадает в промежуток между 3.2p1 (не более одного определения на единицу перевода, как и в вашем вопросе) и 3.2p6 (в котором описано, как классы, перечисления, встроенные функции и различные шаблоны могут иметь повторяющиеся определения в единицы перевода).

Для сравнения, в C, 6.9p5 требуется, чтобы (мой акцент):

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

Ответ 3

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

Undefined также можно ожидать, когда этот Международный Стандарт не дает описания какого-либо явного определения поведение.

Таким образом, он может компилироваться и запускаться красиво или может прекратиться во время перевода с сообщением об ошибке или может привести к сбою во время выполнения и т.д.

EDIT: см. Джеймс Макнеллис, ответ на этот стандарт действительно содержит правила.

Ответ 4

В компиляции нет ошибки, ошибка связана с ее связью. По умолчанию ваша глобальная переменная или функции являются общедоступными для других файлов (есть extern storage), поэтому в конце, когда компоновщик хочет связать свой код, он видит два определения для x и не может выбрать один из них, поэтому, если вы не используете x из main.cpp в other.cpp, а наоборот - статичны (это означает, что он виден только файлу, который его содержит)

// other.cpp
static int x;

// main.cpp
static int x;