Нет, это не связано с грамматикой C ++, но с ленивым созданием шаблонов C ++
и двухфазный поиск.
В C ++ зависимое имя - это имя или символ, значение которых зависит от одного или нескольких
параметры шаблона:
template <typename T>
struct Foo {
Foo () {
const int x = 42;
T::Frob (x);
}
};
Анализируя этот фрагмент в одиночку, не зная всех будущих значений T, ни один компилятор C ++ не сможет определить, является ли frob
в T
именем функции, именем типа, чем-то еще или существует ли он вообще.
Чтобы привести пример того, почему это важно, представьте себе некоторые типы, которые вы замените на T:
struct Vietnam {
typedef bool Frob; // Frob is the name of a type alias
};
struct Football {
void Frob (int) {} // Frob is a function name
};
struct BigVoid {}; // no Frob at all!
Поместите их в наш Foo-шаблон:
int main () {
Foo<Vietnam> fv; // Foo::Foo would declare a type
Foo<Football> ff; // Foo::Foo would make a function call
Foo<BigVoid> fbv; // Foo::Foo is not defined at all
}
В этом отношении важна концепция двухфазного поиска . На первом этапе
независимый код анализируется и компилируется:
template <typename T>
struct Foo {
Foo () {
const int x = 42; // does not depend on T
T::Frob (x); // full check skipped for second phase, only rudimentary checking
}
};
Эта первая фаза позволяет компиляторам выдавать сообщения об ошибках в самом определении шаблона.
Второй этап вызовет ошибки вашего шаблона в сочетании с известным тогда типом T.
Некоторые ранние компиляторы C ++ анализируют шаблоны только после того, как вы их создадите; с этими компиляторами устранение неоднозначности не требовалось, потому что в момент создания экземпляра аргументы шаблона известны. Проблема с этим однофазным поиском состоит в том, что многие ошибки в самом шаблоне не будут обнаруживаться вообще или только в конце компиляции, потому что шаблоны по умолчанию создаются лениво, т.е. только части класса -Tamplate - это расширенные, которые фактически используются, плюс это дает вам больше загадочных сообщений об ошибках, которые, возможно, имеют корень в аргументе шаблона.
Итак, чтобы двухфазный поиск работал, вы должны помочь компилятору.
В этом случае вы должны использовать typename
, чтобы сообщить компилятору, что вы имеете в виду тип:
template <typename T>
struct Foo {
Foo () {
const int x = 42;
typename T::Frob (x);
}
};
Компилятор теперь знает, что x - это переменная типа Frob:)