views:

138

answers:

2

I have a problem with a template and pointers ( I think ). Below is the part of my code:

/* ItemCollection.h */

#ifndef ITEMCOLLECTION_H
#define ITEMCOLLECTION_H

#include <cstddef>

   using namespace std;

   template <class T> class ItemCollection
   {
   public:
    // constructor
        //destructor 

     void insertItem( const T );

   private:
         struct Item
         {
          T price;
          Item* left;               
          Item* right;          
         };
         Item* root;     
         Item* insert( T, Item* );

   };
 #endif

And the file with function defintion:

/* ItemCollectionTemp.h-member functions defintion */

#include <iostream>
#include <cstddef>

#include "ItemCollection.h"

template <class T>
   Item* ItemCollection <T>::insert( T p, Item* ptr)
   {
      // function body
   }

Here are the errors which are generated by this line of code:

Item* ItemCollection <T>::insert( T p, Item* ptr)

Errors:

error C2143: syntax error : missing ';' before '*'

error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

error C2065: 'Type' : undeclared identifier

error C2065: 'Type' : undeclared identifier

error C2146: syntax error : missing ')' before identifier 'p'

error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

error C2470: 'ItemCollection::insert' : looks like a function definition, but there is no parameter list; skipping apparent body

error C2072: 'ItemCollection::insert': initialization of a function

error C2059: syntax error : ')'

Any help is much appreciated.

+7  A: 
template <class T> 
   typename ItemCollection <T>::Item* ItemCollection<T>::insert( T p, Item* ptr) 
   { 
      // function body 
   } 
Alexey Malistov
Maybe you could edit in an explanation of the changes you made?
Tyler McHenry
What explanation? I only replace wrong `Item* ItemCollection <T>::insert( T p, Item* ptr)` by right `typename ItemCollection <T>::Item* ItemCollection<T>::insert( T p, Item* ptr)`.
Alexey Malistov
I know, but it's more helpful to someone learning C++ to explain why their code was having a problem then just to give them code that they can drop in to fix the problem; otherwise nothing is learned and they come back to SO for the same issue again. Especially with respect to the need for the `typename` keyword which is something often not understood in C++. And even if the OP understands what he did wrong, an explanation would help people who find this question through Google.
Tyler McHenry
I believe the class "scope" begins after the type is named, so the return type needs to be fully spelled out, but the arguments needn't be?
visitor
I agree with Tyler, explanation would be nice
Kary
-1 for not telling the OP why the change is necessary. An answer isn't much good if the asker doesn't *learn* anything from it.
jalf
+4  A: 

The short answer is what Alexey already posted:

template <typename T>
typename ItemCollection<T>::Item* ItemCollection<T>::insert( T p, Item * ptr ) {
   // ...
}

(To understand why typename is required, search SO for related questions, or else drop a comment. I will focus the answer in the lookup rules that explain why the return and the argument types must be declared differently)

The explanation is that the lookup rules in c++ have different scopes for the return type and the rest of the parameters. When the compiler sees the definition A B::c( D ), A is checked in the enclosing namespace of the definition, as is B. When the compiler finds ::c it looks up c inside class B. At that point, the rest of the definition is inside the scope of the class B for the rest of parameters. That means that if the return type is an internal type of the class, you have to use the qualified name for the return type, while in the case of D the compiler will first look it up inside the class B.

That explains why the return type must be fully qualified even if the last parameter is not. When the parameter Item * ptr is found by the compiler, it is already in the scope of the class, and it will find it there. On the other hand, there is no Item defined in the enclosing namespace.

One of the changes in the upcomming standard will deal with this, even if it was not designed with this purpose in mind. If I recall correctly, the following syntax should compile without the full type qualification:

template <T>
auto ItemCollection<T>::insert( T p, Item * ptr ) -> Item *
{
   return 0;
}

The reason is exactly the same. After ItemCollection<T>::insert has been parsed the remainder tokens will be verified inside the ItemCollection<T> scope, including the -> Item * return definition.

David Rodríguez - dribeas