Связь различных константных / статических переменных - PullRequest
6 голосов
/ 22 августа 2010

У меня есть несколько вопросов о связи из следующих переменных.На примерах 7.1.1 / 7 C ++ 03 и экспериментов с компиляторами (Comeau, Clang и GCC) я пришел к следующим видам связей:

  1. Первый static,тогда extern

    static int a; // (a)
    extern int a; // (b) valid, 'a' still internal
    

    Для меня ясно, что в соответствии с разделом 3.5: (а) подразумевает внутреннюю связь.И (б) также подразумевает внутреннюю связь, потому что имя «а» объявлено статическим (в соответствии с (а)).

  2. Сначала extern, затем static

    extern int b; // (c)
    static int b; // (d) invalid!
    

    Сначала (c) подразумевает внешнюю связь.Но (d) подразумевает внутреннюю связь, потому что имя "b" объявлено статическим (d).Это неверно в соответствии с 7.1.1 / 7, поскольку подразумеваемая связь не является согласованной.

  3. Сначала const, затем extern

    const double pi1 = 3.14; // (e)
    extern const double pi1; // (f) valid and 'pi1' is internal
    

    Во-первых, (e) подразумевает внутреннюю связь, так как она является const, и не объявлена ​​явной как extern, так иранее подразумеваемая внешняя связь.И (f) должно подразумевать внешнее связывание и быть ошибкой, потому что оно явно объявляет имя extern, но компиляторы сохраняют его внутренним! Почему так? Это мой вопрос.

  4. Сначала extern, затем const

    extern const double pi2; // (g)
    const double pi2 = 3.14; // (h) valid and 'pi2' is external
    

    Сейчас(g) подразумевает внешнюю связь, потому что мы явно объявили extern.И (h) также подразумевает внешнюю связь, потому что (g) явно объявлен extern.


Я экспериментально обнаружил связь для 3 и 4 со следующим шаблоном (второй аргумент необходим для внешней связи)

template<typename T, T&> struct ensure { };

ensure<const double, pi1> e1; // failed
ensure<const double, pi2> e2; // succeeded

Резюме: Обсуждение с Чарльзом Бэйли оказалось весьма плодотворным и показало, что есть два возможных толкования 3.5/3, где важный пункт маркированного текста гласит:

Имя, имеющее область пространства имен (3.3.5), имеет внутреннюю связь, если оно является именем

  • объекта или ссылки, которая явно объявлена ​​как const и ни явно не объявлена ​​как extern, ни ранее объявленаиметь внешнюю связь;

Если мы посмотрим на точку (f), то две интерпретации приходят к разным выводам, как показано ниже

  1. Первая интерпретация отмечает, чтоpi1 объявлен const, но также объявлен extern.Таким образом, переменная имеет внешнюю связь.

  2. Второе толкование интерпретирует оба случая «объявленного» как ссылку на одно и то же объявление.Таким образом, это означает, что оно объявлено const, но не extern const.Мы отмечаем, что (e) объявлен const, а не extern const, поэтому мы даем pi1 внутреннюю связь.

Какое толкование верно?Я не могу определить из этой формулировки, но компиляторы, похоже, интерпретируют это по-другому.В частности, если мы возьмем первую интерпретацию, тогда последняя цитируемая часть 3.5/3 будет излишней, потому что не будет никакого действительного сценария, в котором имя будет объявлено const и ранее объявлено с внешней связью, но без явногоextern.

Ответы [ 3 ]

4 голосов
/ 22 августа 2010
const double pi1 = 3.14; // (e)
extern const double pi1; // (f) valid and 'pi1' is internal

Моя интерпретация следующая.При рассмотрении связи имени мы учитываем предыдущие объявления, а также то, которое интерпретируется в данный момент при разборе.Вот почему static int a; extern int a; в порядке, а extern int b; static int b; - нет.

При обнаружении первого объявления мы отмечаем, что pi1 явно объявлено const, но ни явно не объявлено extern, ни ранее не объявленоиметь внешнюю связь.Это соответствует одному из вариантов 3.5 / 2, поэтому pi1 имеет внутреннюю связь.

При обнаружении второго объявления мы спрашиваем: pi1 имя объекта, который явно объявлен const, но ни явнообъявил extern ни [... бла ...].Я утверждаю, что это потому, что это было так заявлено в пункте (е).Конечно, это не объявляется таким образом везде, но таким же образом a было именем объекта, объявленного static, когда мы рассматривали объявление extern int a;, хотя оно не было объявлено static везде.Для меня это означает, что объявление (f) не подразумевает связь, отличную от объявления (e).

0 голосов
/ 29 октября 2010

Наличие обоих (e) и (f) в одной и той же области пространства имен просто недопустимо, согласно §7.1.1 / 7 «Связи, подразумеваемые последовательными объявлениями для данного объекта, должны согласовываться».

Это правило требует диагностики.

Однако, по крайней мере, Comeau Online не диагностирует нарушение.

Приветствия и hth.,

РЕДАКТИРОВАТЬ : Heон, я посмотрел DR 426 , как упомянуто в другом ответе здесь, и, похоже, те, кто разработал предлагаемое решение, сделав его UB вместо диагностируемого, не знали о §7.1.1 / 7.Я не собираюсь комментировать проблему или даже поднимать ее в comp.std.c ++, потому что я считаю, что работа по стандартизации слишком политическая и бессмысленная (аргументы mumbo-jumbo) для меня.Но в любом случае код недействителен.

0 голосов
/ 22 августа 2010

Я думаю, что в # 3 вы допустили ошибку в своем анализе. Насколько я знаю, const ничего не подразумевает в связи. Я не уверен, как вы пришли к выводу, что компилятор делает связь внутренней. Большинство компиляторов (в качестве оптимизации) заменят все ссылки на переменную const значением, к которому она была инициализирована, поэтому символ может вообще не появляться в коде.

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

И если const подразумевает внутреннюю связь, то # 4 должно быть ошибкой по той же причине, что и # 2.

...