В качестве разогрева к моему полному ответу рассмотрим следующее:
template <typename T> void IterateOverContainer(T container) {
/* Error! */
T::iterator itr(container.begin());
}
Здесь iterator
- это тип, вложенный в T
; например, std::vector<int>::iterator
. Чтобы избежать неясностей, ключевое слово typename
становится необходимым:
template <typename T> void IterateOverContainer(T container) {
/* Now good! */
typename T::iterator itr(container.begin());
}
Теперь это «ясно» имя типа (это то, что означает typename
!), Поэтому ясно, что мы хотим объявить переменную, а не вызывать функцию.
Тем не менее, с новыми функциями C ++ 11 вы можете полностью обойти это с помощью auto
:
template <typename T> void IterateOverContainer(T container) {
auto itr(container.begin());
}
Или, если быть более понятным:
template <typename T> void IterateOverContainer(T container) {
auto itr = container.begin();
}
Теперь, что касается вашего вопроса: как T::x(y)
может когда-либо объявить переменную? Ну, из-за странной причуды C, это совершенно допустимое объявление переменной:
int (x);
Это так же, как
int x;
Итак, если бы у нас было что-то вроде этого:
template <typename T> void IterateOverContainer(T container) {
/* Error! */
T::iterator(itr);
}
Это можно интерпретировать как объявление переменной с именем itr
типа T::iterator
или как вызов функции T::iterator
с передачей itr
в качестве параметра. Использование typename
устраняет неоднозначность, какой это.
Интересно, что это положение для дополнительных скобок является той же самой причиной, по которой существует Most Vexing Parse . Я надеюсь, что вы никогда не столкнетесь с этим. : -)
Надеюсь, это поможет!