返回list::迭代器

C++ templates: Returning list::iterator

本文关键字:迭代器 list 返回      更新时间:2023-10-16

如何使下面的代码工作?在编译过程中,我得到一个错误,告诉我searchForResource函数没有返回类型。

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);
};

另外,这是我如何定义searchForResource函数吗?

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

std::list<ResourceWrapper*>::iterator是编译器很难理解的。在实现和声明中加上 typename 前缀,让编译器知道它是一个类型。

一样:

typename std::list<ResourceWrapper*>::iterator searchForResource(const std::string& file);
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 ...   
}

有一条经验法则可以避免这种编译错误。

每当声明变量或函数时,模板后面跟着作用域解析操作符::,则始终在定义前放置关键字typename

例如,

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

同样的事情也适用于函数声明

我想你漏掉了一个typename关键字。

问题是ResourceWrapper是一个依赖名称*(它的定义取决于类型参数T),这使得std::list< ResourceWrapper * >成为一个依赖类型名称。模板分两次检查,在第一次检查中,检查没有实际类型替换的模板的正确性。现在,当您输入std::list< ResourceWrapper* >::iterator时,编译器无法预先知道iterator实际上是一个类型,而不是静态属性或类std::list< ResourceWrapper* >的成员,因为类型是依赖的,而T还没有被替换。

您必须通过使用typename关键字提示编译器通知它iterator确实是一种类型,正如其他人之前已经提到的那样:

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

没有看到剩下的代码,我不能说,但似乎ResourceWrapper实际上不应该是T的依赖类型。如果它实际上是非依赖的,则应该将类型移到类模板之外。在这种情况下,将不再需要typename:

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

因为它是在模板之外定义的,所以ResourceManager模板的所有可能的实例化都是一个定义,现在ResourceWrapper不再依赖于T,并且typename不再需要(也不正确)。

*为什么ResourceWrapper是依赖的,这将如何影响代码。

ResourceWrapper依赖于类型T的原因通过讨论完全限定名::ResourceManager<T>::ResourceWrapper可以更容易地看出。T是类型的一部分,因此T影响ResourceWrapper的实际定义。这在某种程度上是一个人为的例子,你可以说,如果编译器正在解析这个特定的模板,那么它必须知道ResourceWrapper是一个类型,因此std::list< ResourceWrapper*>::iterator是一个类型……问题来了。没有特别的理由不为ResourceManager的特定实例化对std::list模板进行专门化:

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;
...
};
}

同样是人为的,但是编译器在解析模板时不可能预先知道,在您实际使用特定类型实例化模板之前,这种专门化不会出现。

您有ResourceWrapper结构的前向声明,这对于编译好的行来说足够好,但是您正在获得错误,因为在那一点上编译器需要ResourceWrapper结构的完整类型声明。(可能你的答案,这段代码实际上编译良好VS2008)