Paul Manta
Paul Manta

Reputation: 31577

C++ templates: Returning list::iterator

How can I make the following code work? During compilation I get an error telling me that the searchForResource function has no return type.

template<class T>
class ResourceManager
{
  private:
    struct ResourceWrapper;
    std::list<ResourceWrapper*> resources_; // This compiles fine

    std::list<ResourceWrapper*>::iterator  // Error occurs here
        searchForResource(const std::string& file);
};

Also, is this how I would define the searchForResource function?

template<class t>
std::list<typename ResourceManager<T>::ResourceWrapper*>::iterator
    ResourceManager<T>::searchForResource(const std::string& file)
{
    // ...
}

Upvotes: 3

Views: 5496

Answers (6)

cgmb
cgmb

Reputation: 4424

std::list<ResourceWrapper*>::iterator is hard for the compiler to understand. Prefix it with typename in both the implementation and the declaration to let the compiler know that it's a type.

Like so:

typename std::list<ResourceWrapper*>::iterator searchForResource(const std::string& file);

Upvotes: 9

The problem is that ResourceWrapper is a dependent name *(it's definition depends on the type argument T), and that makes std::list< ResourceWrapper * > a dependent type name. Templates are checked in two passes, during the first pass, correctness of the template without actual type substitution is checked. Now when you type std::list< ResourceWrapper* >::iterator the compiler cannot know upfront that iterator is in fact a type and not an static attribute or member of the class std::list< ResourceWrapper* > because of the type being dependent and the T not yet being substituted.

You have to hint the compiler as to inform it that the iterator is indeed a type by using the typename keyword, as others have already mentioned before:

typename std::list< ResourceWrapper* >::iterator

Without seeing the rest of the code, I cannot say, but it seems as if ResourceWrapper should actually not be a dependent type on T. If it is in fact non-dependent, you should move the type outside of the class template. in that case, the typename will no longer be required:

struct ResourceWrapper;
template <typename T>
class ResourceManager {
   std::list<ResourceWrapper*>::iterator searchForResource(const std::string& file);
...

Because it is defined outside of the template there is a single definition for all possible instantiations of the ResourceManager template, now ResourceWrapper is no longer dependent on T, and typename is no longer needed (nor correct).

* Why is ResourceWrapper dependent and how could this affect the code.

The reason that ResourceWrapper is dependent on the type T is easier seen by discussing the fully qualified name: ::ResourceManager<T>::ResourceWrapper. The T is part of the type, and as such T affects the actual definition of ResourceWrapper. This is somehow a contrived example in that you can arguably say that if the compiler is parsing this particular template, then it must know that ResourceWrapper is a type, and thus that std::list< ResourceWrapper*>::iterator is a type... and here is the problem. There is no particular reason not to have an specialization of the std::list template for a particular instantiation of ResourceManager:

namespace std { // you should in general not add things to the std namespace!
                // but the implementation can
template <>
struct list< ResourceManager<int>::ResourceWrapper > {
   static const int iterator = 5;
...
};
}

Again, contrived, but the compiler cannot possibly know upfront while parsing the template that such an specialization will not be present before you actually instantiate the template with a particular type.

Upvotes: 1

iammilind
iammilind

Reputation: 69988

There is a rule of thumb to avoid such compilation errors.

Whenever you are declaring a variable or function, with a template followed by scope resolution operator :: then always put a keyword typename in front of the definition.

For example,

MyNameSpace::MyClass<T> x; // Ok; because template is NOT followed by scope resolution
MyNameSpace::MyClass<T>::MyType x; // Error; MyType can be a variable or a type; so put typename ahead

Same thing is applicable to function declaration also.

Upvotes: 4

Kirill V. Lyadvinsky
Kirill V. Lyadvinsky

Reputation: 99585

template<class T>
class ResourceManager
{
  private:
    struct ResourceWrapper;
    std::list<ResourceWrapper*> resources_;

//      | typename lost here
//      V
    typename std::list<ResourceWrapper*>::iterator
        searchForResource(const std::string& file);
};

template<class T>
//  | typename lost here                    asterisk lost here | 
//  V                                                          V 
typename std::list<typename ResourceManager<T>::ResourceWrapper*>::iterator
    ResourceManager<T>::searchForResource(const std::string& file)
{
   return ...   
}

Upvotes: 5

DXM
DXM

Reputation: 4543

you have forward declaration of ResourceWrapper struct which is good enough for the line that compiles fine, but you are getting the error because at that point compiler needs full type declaration for ResourceWrapper struct. (possibly your answer, this code actually compiles fine with VS2008)

Upvotes: 0

static_rtti
static_rtti

Reputation: 56262

I think you're missing a typename keyword.

Upvotes: 3

Related Questions