奇怪的行为?

时间:2013-01-10 09:04:08

标签: c++ templates iterator typedef

  

可能重复:
  Where and why do I have to put the “template” and “typename” keywords?

我已经(不得不:)几周前成为了一名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作为普通类,那就完全没问题了。谢谢你的帮助。

2 个答案:

答案 0 :(得分: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可能意味着不同的东西(类型名称,但是 - 比如静态成员......),这取决于你传递的是什么。

这就是为什么在模板中有时你需要明确你的意图,并表明你实际上是指“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() {

答案 1 :(得分: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()
 };
  • 内部typedef 如果您想要从map更改为另一个容器(例如unorderd_map),它会派上用场,并避免重复输入typename嵌套的typedef。
  • auto (C ++ 11关键字)可以限制输入复杂的迭代器类型
  • 对于迭代器,
  • 按值返回是惯用的方式
  • begin()/ end()的
  • const-correctness
  • 重载 begin()/ end()用于非const迭代器,并提供cbegin()/ cend()

一般情况下,最好使用与包装函数相同的接口(constness,返回值)(在本例中为map的begin()/ end())。