Использование ключевого слова typename с параметрами функции шаблона
В С++ требуется ключевое слово typename
, чтобы компилятор мог устранить неоднозначность между вложенными типами и вложенными значениями в шаблонах. Однако существуют определенные ситуации, когда двусмысленность невозможна, например, когда производный класс наследуется от вложенного типа класса.
template <class T>
class Derived : public T::type
{ };
Здесь ключевое слово typename
не требуется и на самом деле даже не разрешено. Это имеет смысл, потому что контекст устраняет двусмысленность. Здесь T::type
должен ссылаться на тип, поскольку вы, очевидно, не можете наследовать от значения.
Я бы подумал, что то же самое верно для параметров шаблона функции.
template <class T>
void foo(const T::type& v)
{
}
В этом случае контекст дает понять, что T::type
должен ссылаться на тип, поскольку параметр функции не может быть значением. Тем не менее, компилятор этого не принимает. Он хочет const typename T::type&
. Это кажется непоследовательным. Почему язык допускает неявное предположение о вложенном типе в контексте наследования, но не в контексте параметров функции? В обоих случаях не может быть двусмысленности, поэтому зачем нужна typename
в одном, а не другом?
Ответы
Ответ 1
Если вы слегка измените свое выражение, вы получите совершенно другую историю
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
бесплатно: имя, которое имя поиска возвращает к более высокоуровневым модулям компилятора, либо относится к типу, либо поиск имени дал бы ошибку.
Я написал запись в FAQ о Где поставить "шаблон" и "typename" на зависимые имена.
Ответ 2
Во-первых, я не думаю, что когда-либо было намерение сделать резкое и точное различие между ситуациями, в которых допускаются только имена типов (например, имя базового класса) и ситуации, в которых допускаются непиковые сущности (например, выражения). Я бы сказал, что контекст имени базового класса был выделен по другой причине.
Во-вторых, не совсем правильно сказать, что в объявлениях параметров функции каждая сущность обязательно является typename. Вы можете объявить параметр следующим образом
template <class T>
void foo(const T::type& v[T::value]);
Конечно, грамматика в этом случае явно указывает, что type
должно быть typename и value
должно быть значением. Однако компилятор может понять это только после синтаксического анализа объявления, в то время как я полагаю, что идея typename
была введена, чтобы помочь компилятору фактически начать правильный синтаксический анализ кода, то есть различие должно быть доступно до синтаксический анализ, как ввод в синтаксический анализ. Это различие может иметь глубокое влияние на интерпретацию кода.
Ответ 3
Было бы интересно найти, что вызывает это.
Я пытаюсь прочитать стандарт в поисках ответа, обратите внимание, что я новичок в этом.
Однако я считаю, что нашел соответствующее предложение.
§14.6.2. Имя, используемое в шаблоне декларации или определения и в зависимости от шаблона-параметра предполагается, что не следует указывать тип , если только поиск подходящего имени находит тип имя или имя ключевое слово typename.
Я предполагаю, что это означает, что проблема заключается в различии того, как работает поиск имени для списков спецификаторов базы данных и аргументов функции.
Поиск имени базового спецификатора:
§ 10.2. Во время поиска базы имя класса, имена не-типа игнорируются(3.3.10).
Это объясняет, почему typename не требуется для базовых спецификаций.
Ищете поиск имени аргумента функции.
Пожалуйста, исправьте меня, если это неправильное или несущественное предположение. Тем временем я продолжаю копать.
Ошибка, заданная VS2010, когда не квалифицируется аргумент шаблона в объявлении функции, выглядит следующим образом:
'T:: type': зависимое имя не является введите префикс типа 'typename' укажите тип.
Тем не менее, я все еще не понимаю правила поиска зависимого имени аргумента функции...