Ключевое слово typename и спецификатор вложенного имени

struct A{};

template <typename T>
struct B
{
    typename ::A a1; //(1)
    typename A a2; //(2): error
};

int main(){return 0;}

Почему первый случай правильный, а второй нет? Я не понимаю смысл этого ограничения.
И вообще, почему разрешен первый случай? ::A не зависит от имени шаблона. Что в этом смысла?

2 ответа

Решение

Как объясняет ответ @MikeSeymour, строго следуя стандарту (C++11, у меня нет текста на C++14), case (1) также должен быть ошибочным - typename Префикс квалифицированного имени может использоваться только в том случае, если в левой части ::,

Однако, как указал @hvd в комментариях, проблема CWG 382 указывает на то, что реальное намерение состоит в том, чтобы typename перед любым квалифицированным именем, включая квалификацию глобального пространства имен. Поскольку это то, что большинство компиляторов, кажется, реализуют, остальная часть этого ответа следует с этой идеей.

С этой точки зрения дело не в том, что случай (2) является ограничением, а в том, что дело (1) является благотворительным. Требуемое правило (и его очень оригинальная формулировка, я полагаю) заключается в том, что "если квалифицированное имя, которое зависит от параметров шаблона, обозначает тип, вы должны поставить перед ним префикс typename "Для удобства его ослабляют" typename может использоваться для любого квалифицированного имени, которое обозначает тип, независимо от того, зависимо оно или нет. " (1)

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


(1) Это ослабление на самом деле прямо не указано в стандарте, но следует из комбинации нескольких правил. По сути, везде, где допускается спецификатор простого типа (что-то, обозначающее тип), грамматика также допускает спецификатор typename (квалифицированное имя с префиксом typename). Правила для шаблонов (в основном в 14.6) только утверждают, что typename требуется, когда зависимое квалифицированное имя обозначает тип. Так как ничто не запрещает typename в других контекстах его можно использовать с любым квалифицированным именем, которое обозначает тип (даже вне контекста шаблона).

Правило не в том, что вы можете использовать только typename если тип вложен в зависимую область. Правила более или менее:

  • ты должен использовать typename если это в зависимой области
  • вы можете использовать только typename где это разрешено грамматикой.

Грамматика допускает это для подмножества квалифицированного идентификатора, определенного

typename-specifier:
    typename nested-name-specifier identifier
    typename nested-name-specifier template<opt> simple-template-id

nested-name-specifier:
    :: (C++14 or later)
    ::<opt> type-name ::
    ::<opt> namespace-name ::
    decltype-specifier ::
    nested-name-specifier identifier ::
    nested-name-specifier template<opt> simple-template-id ::

Таким образом, второй случай, безусловно, запрещен, поскольку он не предполагает вложенности. Строго говоря, до C++14 первое также было запрещено, так как глобальный классификатор :: не соответствует этой грамматике.

Другие вопросы по тегам