【问题标题】:Why is the use of typedef in this template necessary?为什么在这个模板中使用 typedef 是必要的?
【发布时间】:2009-07-31 21:50:36
【问题描述】:

当我在 Visual Studio 2005 中编译这段代码时:

  template <class T>
  class CFooVector : public std::vector<CFoo<T>>
  {
  public:
    void SetToFirst( typename std::vector<CFoo<T>>::iterator & iter );
  };

  template <class T>
  void CFooVector<T>::SetToFirst( typename std::vector<CFoo<T>>::iterator & iter )
  {
    iter = begin();
  }

我收到以下错误:

c:\home\code\scantest\stltest1\stltest1.cpp(33) : error C2244:     'CFooVector<T>::SetToFirst' : unable to match function definition to an existing declaration
    c:\home\code\scantest\stltest1\stltest1.cpp(26) : see declaration of 'CFooVector<T>::SetToFirst'
    definition
    'void CFooVector<T>::SetToFirst(std::vector<CFoo<T>>::iterator &)'
    existing declarations
    'void CFooVector<T>::SetToFirst(std::_Vector_iterator<_Ty,_Alloc::rebind<_Ty>::other> &)'

如果我将 typedef 添加到 CFooVector 模板,我可以让代码编译并工作:

  template <class T>
  class CFooVector : public std::vector<CFoo<T>>
  {
  public:
    typedef typename std::vector<CFoo<T>>::iterator FooVIter;
    void SetToFirst( FooVIter & iter );
  };

  template <class T>
  void CFooVector<T>::SetToFirst( FooVIter & iter )
  {
    iter = begin();
  }

我的问题是,为什么 typedef 在使用裸 'typename std::vector&gt;::iterator' 声明不起作用时起作用?

【问题讨论】:

  • 顺便说一句,Comeau C++ 编译得很好(iter = begin() 行除外,它应该是iter = this-&gt;begin() - 但这是不相关的)。可能是 VC++ 中的一个错误。请有人尝试g++
  • typename 应该可以工作。我很惊讶它没有死在 vector> 上。您可能必须在 >> 之间添加一个空格。 (也许他们在您使用的 C++ 编译器版本中修复了它......)
  • @Pavel:现在 vhanda 和我都在 g++ 上进行了测试。 MSVC 对这是否应该编译持有少数意见...

标签: c++ templates typedef typename


【解决方案1】:

这也可以编译并揭示 VC++ 混淆的根源——分配器类型。显然在类 VS 之外选择了不同的默认值。或者它可能无法识别它们是相同的。

在 VS2008(原样)和 VS2003(>> 之间有空格)上编译

template <class T>
class CFoo
{
public:
    T m_t;
};

template <class T>
class CFooVector : public std::vector<CFoo<T>>
{
public:
    void SetToFirst(typename std::vector<CFoo<T>, typename CFooVector::_Alloc>::iterator & iter);

};

template <class T>
void CFooVector<T>::SetToFirst( typename std::vector<CFoo<T>, typename CFooVector::_Alloc>::iterator & iter )
{
    iter = begin();
}

GCC 3.4 需要 this->begin() 和空间,但否则它可以在没有显式分配器类型的情况下编译代码...绝对看起来像 MS 编译器没有应有的智能...

【讨论】:

  • 我想你有它。那么看起来可能是编译器错误?
  • 也在 VS2005 上编译。谢谢。
  • 请将其作为错误报告给 Microsoft Connect。它也在 VS2010 beta 中重现。
  • 我不会报告它(它要我注册连接,这在 Opera 中已损坏,而且我很久以前忘记了登录 IE 的密码 :))。有帐户的人可以提交错误吗?
  • 我会举报的。感谢您的出色回答,尤金。
【解决方案2】:

问题不是关于 typedef 而是 typename。

每当编译器遇到依赖于模板的名称时(基本上,任何在模板之后使用:: 的东西,它都无法确定这是一个类型还是一个值(它可能是一个静态 int,例如),所以它需要一个提示。

当您添加 typename 时,您指定依赖成员实际上是一个类型。

在您的 typedef 示例中,您在 typedef 声明中仍然有 typename,但是一旦声明了 typedef,它就不是依赖名称。它只是一个类型,所以 typename 在引用 typedef 时不是必需的。

基本上,编译器无法确定std::vector&lt;CFoo&lt;T&gt;&gt;::iterator 是一个类型。

但它知道FooVIter 是一个类型,因为它是一个typedef。 Typedef 始终是类型。

【讨论】:

  • 要为人们进一步量化第一部分,请参阅第 14.6 节第 1 节:“假定模板中使用的名称不命名类型,除非适用的名称查找找到类型名称或名称是由关键字 typename 限定。”
  • “基本上,编译器不能确定 typename std::vector>::iterator 是一个类型。”为什么这样 ?这不是 typename 关键字的用途吗?
  • 我还是一头雾水。我认为“typename”关键字的全部目的是告诉编译器将依赖名称解释为一种类型。所以,我不确定我理解为什么“typedef”有效而独立的“typenames”没有。
  • 是的,你是对的,独立类型名工作正常。没有注意到我复制了“typename”以及该行的其余部分
  • 这解释了为什么第一个 sn-p 不会编译。它没有解释为什么第二个 sn-p 不会编译。
【解决方案3】:

typename 是这里最重要的关键字。

需要注意的是:

>>

它们之间应该有一个空格,以免编译器误以为你正在使用

operator>>

如果没有你的整个代码,我不知道为什么使用 typename 后会出现问题。也许你在类定义之外的定义中不需要它?

另外,这样做可能会更好:

template <typename T>

而不是

template <class T>

顺便说一句,从 STL 类派生不是一个好主意,除非你非常非常小心。

STL 类没有虚拟析构函数。

【讨论】:

  • 谢谢。我没有想到“>>”。这是实际问题的一个大大简化的版本;实际代码不是直接从向量派生的。
  • 关于不从 STL 类和 >> 事物派生的好处。为什么他更喜欢模板 而不是类?他们做同样的事情。
  • 你确实需要函数定义中的“typename”——如果你删除它,GCC 会抛出一个合适的,认为你正在定义 CFooVector&lt;T&gt; 的一个 void 静态数据成员,称为 SetToFirst,带有整数构造函数参数,由两个整数按位与组成,这两个整数均未定义。 5 个错误:-)
  • 我更喜欢使用 ,而不是 ,尽管我认为它们的工作方式相同。如果 T 是 POD,例如 int,一些编译器可能会发出警告。当我使用类(而不是结构)转发声明结构时,Visual C++ 给我一个警告。
  • 娟,我觉得没有实际区别。请参阅 Stanley Lippman 解释这是如何发生的:blogs.msdn.com/slippman/archive/2004/08/11/212768.aspx
【解决方案4】:

对我来说似乎可以正常工作如果我进行了以下更改 -

template <class T>
class CFoo
{
public:
T m_t;
};

template <class T>
class CFooVector : public std::vector< CFoo<T> >
{
public:
void SetToFirst( typename std::vector<CFoo<T> >::iterator & iter );
};

template <class T>
void CFooVector<T>::SetToFirst( typename std::vector<CFoo<T> >::iterator & iter )
{
iter = begin();
}

我基本上将std::vector&lt;CFoo&lt;T&gt;&gt; 更改为std::vector&lt;CFoo&lt;T&gt; &gt;,否则编译器(在我的例子中是gcc)会将其识别为>> 运算符。

【讨论】:

  • 刚试过这个,我在 VS2005 中使用 '>>' 和使用 '>> >' 得到相同的结果。
【解决方案5】:

另一个有趣的地方:如果您在SetToFirst() 的定义中不对vector 使用std:: 名称限定符,VS2008 会编译您的第二次尝试:

  template <class T>
  class CFooVector : public std::vector< CFoo<T> >
  {
  public:
      void SetToFirst( typename std::vector< CFoo<T> >::iterator & iter );
  };

  template <class T>
  void CFooVector<T>::SetToFirst( typename /*std::*/vector< CFoo<T> >::iterator & iter )
  {
    iter = begin();
  };

请注意,它似乎只对定义很重要,而不是声明。我还感兴趣的是,是否有“using namespace std;”并不重要......

我真的不知道该怎么做。

【讨论】:

  • 好像使用了基类std::vector&lt;CFoo&lt;T&gt;&gt;的注入类名。通常,它需要typename CFooVector::vector&lt; .... 才能成功找到该名称,因为不搜索非依赖名称的依赖基类(vector 作为嵌套名称说明符的一部分不依赖。用CFooVector 为其前缀使其依赖。但是 Visual C++ 没有实现这些规则,这也说明了为什么它接受begin() 而不是this-&gt;begin())。
猜你喜欢
  • 1970-01-01
  • 2015-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 2013-07-28
相关资源
最近更新 更多