Короткий ответ - это то, что Алексей уже написал:
template <typename T>
typename ItemCollection<T>::Item* ItemCollection<T>::insert( T p, Item * ptr ) {
// ...
}
(Чтобы понять, почему требуется typename
, найдите SO для связанных вопросов или же оставьте комментарий. Я сфокусирую ответ в правилах поиска, объясняющих, почему возвращаемый тип и типы аргументов должны объявляться по-разному) *
Объяснение состоит в том, что правила поиска в c ++ имеют разные области видимости для возвращаемого типа и остальных параметров. Когда компилятор видит определение A B::c( D )
, A
проверяется во вложенном пространстве имен определения, как и B
. Когда компилятор находит ::c
, он ищет c
внутри класса B
. В этот момент остальная часть определения находится в области видимости класса B
для остальных параметров. Это означает, что если возвращаемый тип является внутренним типом класса, вы должны использовать полное имя для возвращаемого типа, в то время как в случае D
компилятор сначала ищет его внутри класса B
.
Это объясняет, почему возвращаемый тип должен быть полностью определен, даже если последний параметр не является. Когда параметр Item * ptr
найден компилятором, он уже находится в области видимости класса и найдет его там. С другой стороны, в пространстве имен не определено Item
.
Одно из изменений в готовящемся стандарте будет иметь дело с этим, даже если оно не было разработано с этой целью. Если я правильно помню, следующий синтаксис должен компилироваться без полной спецификации типа:
template <T>
auto ItemCollection<T>::insert( T p, Item * ptr ) -> Item *
{
return 0;
}
Причина точно такая же. После анализа ItemCollection<T>::insert
остальные токены будут проверены в области действия ItemCollection<T>
, включая определение возврата -> Item *
.