3

可能重复:
我必须在哪里以及为什么要放置“模板”和“类型名称”关键字?

我(不得不 :))几周前成为了一名 C++ 开发人员(我之前有过一些经验,但不是太多,我更多的是使用 Java),试图学习所有重要的东西并尽可能高效地开发。如果我的问题完全愚蠢,请原谅。我对一个简单的示例模板类有疑问:

template<typename T>
class SameCounter {
private:
    map<T,int> counted;
public:
    SameCounter(list<T> setup) {
        for(list<T>::iterator it = setup.begin(); it != setup.end(); it++) {
            counted[*it]++;
        }
    }
    map<T,int>::const_iterator& begin() { // line 25
        return counted.begin();
    }
    map<T,int>::const_iterator& end() {
        return counted.end();
    }
};

...
// using the class
Reader rdr;
rdr.Read();
SameCounter<char> sc(rdr.GetData());

编译时出现一些错误:

Error   3   error C4430: missing type specifier - int assumed. Note: C++ does not support default-int   d:\learn_cpp\examples\gyakorlas_1.cpp   25
Error   2   error C2143: syntax error : missing ';' before '&'  d:\learn_cpp\examples\gyakorlas_vizsga\gyakorlas_1.cpp  25

(both of them twice)

我对此一无所知,我假设的模板可能有问题,因为如果我将 SameCounter 创建为普通类,那完全没问题。感谢您的帮助。

4

2 回答 2

9

这应该可以帮助您:

typename map<T,int>::const_iterator& begin() {
    return counted.begin();
}
typename map<T,int>::const_iterator& end() {
    return counted.end();
}

C++ 模板很棘手。T 是一个模板参数,map<T, int>::const_iterator可能意味着不同的东西(类型名称,还有 - 比如说 - 静态成员......),具体取决于您传递的 T 。

这就是为什么在模板中有时您需要明确您的意图并表明您实际上的意思是“const_iterator是一种类型,我想要引用它”。关键字“typename”允许这样做。

见: http: //pages.cs.wisc.edu/~driscoll/typename.html


为了使您的代码更简单并避免减少对 的需求typename,您可以从以下内容开始:

private:
    typedef std::map<T, int> MapType;
    MapType counted;

然后就去

typename MapType::const_iterator &begin() {

不幸的是,这typename仍然需要在这里,您需要进一步typedef typename为每个依赖类型将其从进一步的声明中删除(参见@rhalbersma的答案)。


@rhalbersma的评论之后,我还要强调您应该按值返回这些迭代器。返回对临时对象的引用会导致未定义的行为,因为对象超出范围并且您最终会得到“悬空引用”。

所以让它:

typename MapType::const_iterator begin() {
于 2013-01-10T09:10:43.730 回答
2

我在下面注释了您的课程。有几点值得一提:

  template<typename T>
  class SameCounter 
  {
  private:
     typedef map<T,int> MapType; // typedef here to keep specific container in a single place
     typedef typename MapType::const_iterator const_iterator; // to avoid retyping "typename"
     typedef typename MapType::iterator iterator; // to avoid retyping typename
     MapType counted;
  public:
     SameCounter(list<T> setup) {
        // auto here to avoid complicated expression
         for(auto it = setup.begin(); it != setup.end(); it++) {
             counted[*it]++;
         }
     }

    // by value instead of by reference, mark as const member
    const_iterator begin() const {
        return counted.begin();
    }

    // by value instead of by reference, mark as const member
    const_iterator end() const {
        return counted.end();
    }

    // probably best to also forward cbegin()/cend() and non-const begin() / end()
 };
  • 如果您想从map另一个容器(例如)更改为内部类型定义unorderd_map,它们会派上用场,并且它们可以避免重复键入typename嵌套类型定义。
  • auto(C++11 关键字)可以限制输入复杂的迭代器类型
  • 迭代器的按值返回是惯用的方式
  • begin()/end() 的const 正确性
  • 为非常量迭代器重载begin() / end() 并提供 cbegin()/cend()

一般来说,最好使用与要包装的函数相同的接口(常量、返回值)(在这种情况下为 map 的 begin()/end())。

于 2013-01-10T09:17:34.687 回答