【问题标题】:Strange behavior? [duplicate]奇怪的行为? [复制]
【发布时间】:2013-01-10 09:04:07
【问题描述】:

可能重复:
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 创建为普通类,那完全没问题。谢谢你的帮助。

【问题讨论】:

  • lsit&lt;T&gt;::iterator 之前需要一个 typename 并且地图的 const 迭代器也是如此。
  • 在不知道第 25 行是什么(发生错误的位置)的情况下有点难以回答。但总的来说,这两个错误所说的是 A,您试图定义一个没有类型定义的变量,而 B 您忘记解析某些内容,或者您​​试图将函数定义为引用(如果您返回 &val 函数想将其分配为指针使用 *)
  • “奇怪的行为”可能是你能选择的最糟糕的标题。只有几个问题真的很蠢,这看起来不像,但写得不好。
  • 是的,很抱歉。当我想引用内部类型时,我忘记了模板发生的一个小“问题”。如下回答。谢谢大家。
  • 为了将来参考,在编写代码摘录时,最好标记错误消息中提到的行(例如使用//line 25 here之类的注释。

标签: c++ templates iterator typedef


【解决方案1】:

这应该对你有帮助:

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

C++ 模板很棘手。 T 是一个模板参数,map&lt;T, int&gt;::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 的答案)。 p>


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

那就去做吧:

typename MapType::const_iterator begin() {

【讨论】:

  • 天啊,谢谢,我完全傻了。你是对的,我忘记了对于模板 :: 运算符通常意味着我指的是静态成员而不是类型名。当然。再次感谢您。
  • 我有 compiled the code,你需要在 ctor 中的 list&lt;T&gt;::iterator 中再添加一个 typename。使用 typedef 模式,它简化了很多事情(它在标准库中很常用 - 每个标准模板都有很多用于其“依赖”类型的 typedef。)
  • 顺便说一句,较新的编译器往往会给你带来像 error: need 'typename' before 'std::map&lt;T, int&gt;::const_iterator' because 'std::map&lt;T, int&gt;' is a dependent scope (GCC) 这样的错误。
  • @n3whous3:我几乎不会因为你没有意识到这种晦涩难懂的 C++ 特质而称你为愚蠢。尽管这是我最喜欢的语言,但当我看到它时,我认出了它。
  • @Kos 为什么要通过引用而不是按值返回迭代器?
【解决方案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),inner typedefs 会派上用场,并且它们可以避免为嵌套 typedefs 重复键入 typename
  • auto(C++11 关键字)可以限制输入复杂的迭代器类型
  • 按值返回迭代器是惯用的方式
  • begin()/end() 的常量正确性
  • overload begin() / end() 用于非常量迭代器,还提供 cbegin()/cend()

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

【讨论】:

  • 感谢您的帮助。是的,我也进入了 C++11,我喜欢新功能,但是由于我使用 VS2010,我缺少一些功能。
  • 我在平衡 时遇到了一些奇怪的 HTML 错误,导致代码布局不正确
  • @rhalbersma 那是因为你的代码块直接跟在列表元素后面,所以 Markdown 对缩进的解释不同。您可以在它之前添加“代码:”,它会照常工作。
  • @Kos 谢谢,很高兴知道!
猜你喜欢
  • 2011-05-23
  • 2018-10-26
  • 2012-10-03
  • 2015-01-09
  • 2012-06-26
  • 2012-02-18
  • 2015-08-05
  • 2016-10-27
  • 1970-01-01
相关资源
最近更新 更多