struct A{};
template <typename T>
struct B
{
typename ::A a1; //(1)
typename A a2; //(2): error
};
int main(){return 0;}
Почему первый случай правильный, а второй нет? Я не понимаю смысл этого ограничения.
И вообще, почему разрешен первый случай? ::A
не зависит от имени шаблона. Что в этом смысла?
Как @ MikeSeymour’s answer объясняет, строго следуя стандарту (C ++ 11, у меня нет текста на C ++ 14), case (1) также должен быть ошибочным — typename
Префикс квалифицированного имени может использоваться только в том случае, если в левой части ::
,
Однако, как отмечает @hvd в комментариях, CWG выпуск 382 указывает на то, что реальное намерение состоит в том, чтобы typename
перед любым квалифицированным именем, включая квалификацию глобального пространства имен. Поскольку это то, что, по-видимому, реализует большинство компиляторов, остальная часть этого ответа следует за этой идеей.
С этой точки зрения дело не в том, что случай (2) является ограничением, а в том, что дело (1) является благотворительным. Требуемое правило (и его очень оригинальная формулировка, я полагаю) заключается в том, что «если квалифицированное имя, которое зависит от параметров шаблона, обозначает тип, вы должны поставить перед ним typename
«Для удобства его ослабляют»typename
может использоваться для любого квалифицированного имени, которое обозначает тип, независимо от того, зависимо оно или нет. «(1)
Напротив, неквалифицированное имя никогда не может быть неоднозначным относительно того, относится ли оно к типу или нет, поэтому оно никогда не требует typename
а также typename
поэтому там не разрешено.
(1)Это ослабление на самом деле явно не указано в стандарте, но следует из комбинации нескольких правил. В основном, где бы простой тип спецификатор (что-то, что обозначает тип), грамматика также позволяет имяТипа спецификатор (квалифицированное имя с префиксом 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 первое также было запрещено, так как глобальный классификатор ::
не соответствует этой грамматике.