Reputation: 31577
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
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
Reputation: 208353
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
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
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
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