Если вы слегка измените свою декларацию, вы получите совершенно другую историю
template <class T>
void foo(T::type& v);
Это уже не однозначно. Он может объявить переменную типа void
, которая инициализируется побитовым выражением AND
. Вся декларация будет шаблонной. Конечно, все это семантически бессмысленно, но синтаксически все в порядке.
Появление единственного const
синтаксически делает его однозначным, но это слишком большая зависимость от контекста, чтобы заставить это работать в компиляторе. Он должен помнить, что он читает const
или любую другую подобную вещь, и когда он анализирует T::type
после этого, он должен будет помнить, чтобы принять это имя как тип. Это также привело бы к дальнейшему раздутию и без того сложного Стандарта.
Давайте снова изменим объявление вашей функции
template <class T>
void foo(const T::type);
Даже появление const
не обеспечивает однозначного анализа. Должно ли это быть объявление функции с неназванным параметром, или это должно быть объявление функции с недопустимым именем параметра, которое пропускает свой тип? Имя параметра анализируется с помощью declarator-id
, который также может быть полным именем. Таким образом, const
будет принадлежать спецификаторам типа, в то время как T::type
будет анализироваться компилятором как имя параметра при отсутствии typename
. Это тоже полная чепуха, , но синтаксически действительная .
В случае имен базового класса поиск имени сам по себе указывает, что не типовые имена игнорируются. Таким образом, вы получаете упущение typename
бесплатно: имя, которое поиск по имени дает более высокоуровневым модулям компилятора, либо относится к типу, либо поиск по имени даст ошибку.
Я написал в разделе часто задаваемых вопросов о Где поставить "template" и "typename" на зависимые имена .