Использование extern в области блока

clang, gcc и VS2013 все жалуются на переопределение w в main(), но я не смог найти в стандарте что-либо, запрещающее это.

namespace N {
    extern int j;
    int j;
}

int main()
{
    extern int w;
    int w;
}

В этих параграфах говорится об использовании объявления extern в области блока, но они, похоже, не оправдывают сообщение об ошибке:

§3.3.1/4

Учитывая набор объявлений в одной декларативной области,...

[Примечание. Эти ограничения применяются к декларативной области, в которую вводится имя, которое не обязательно совпадает с регионом в котором происходит декларация. В частности, спецификаторы специфицированных типов (7.1.6.3) и декларации друзей (11.3) может ввести (возможно, не видимое) имя в Пространство имен; эти ограничения распространяются на этот регион. Местный внешний декларации (3.5) могут ввести название в декларативный регион где появляется декларация, а также ввести (возможно, не видимое) в закрытое пространство имен; эти ограничения применяются к оба региона. -end note]

§3.3.2/10

[Примечание: объявления друзей относятся к функциям или классам, которые членов ближайшего охватывающего пространства имен, но они не вводят новые имена в это пространство имен (7.3.1.2). Объявление функций на область блока и объявления переменных с помощью спецификатора extern в области блока относятся к объявлениям, которые являются членами охватывающего пространства имен, но они не вводят новые имена в эту область. -конец примечание]

Ответ 1

Я считаю, что это в основном описано в §3.5/6.

В частности:

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

Итак, extern int w; объявляет w, у которого есть ссылка (внешняя связь, в этом случае, поскольку в этой точке не видно видимого объекта).

Затем вы пытаетесь определить локальный w, который не имеет привязки (в §3.5/8).

Это дает две декларации с тем же именем в той же области, но с разными связями. Это запрещено в §3.3.1/4:

Учитывая набор объявлений в одной декларативной области, каждая из которых указывает одно и то же неквалифицированное имя,

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

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

Ответ 2

Вот моя интерпретация: в разделе 3.3.1/3 в стандарте говорится:

Имена, объявленные объявлением, вводятся в область, в которой происходит объявление, за исключением того, что наличие спецификатора-друга (11.3), определенных видов использования спецификатора разработанных типов (7.1.6.3) и использования- директивы (7.3.4) изменяют это общее поведение.

Поскольку объявления extern не указаны в качестве исключения, имя вводится в область блока, поэтому вы получаете ошибку при попытке ее повторного использования.

В приведенном вами параграфе говорится

но они не вводят новые имена в эту область.

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