朗月清风

享受C++享受生活

C++中的关键字typename


近来想做对LIST的更方便使用的模板类,头文件如下:

#include "config.hpp"

namespace System
{
 namespace Collections
 {
  template<class ValueType>
  class List
  {

  public:
   List();
   virtual ~List();
   virtual int Add(ValueType value);
   virtual bool Contains(ValueType value);
   virtual void Delete(int index);
   virtual ValueType GetByIndex(int index);
   virtual int GetCount();
   virtual void Insert(int index, ValueType value);
   virtual void InsertAfter(ValueType item, ValueType value);
   virtual void InsertBefore(ValueType item, ValueType value);
   virtual void Update(int index, ValueType value);
   virtual shared_ptr<ValueType> Prev(ValueType value);
   virtual shared_ptr<ValueType> Next(ValueType value);

  private:
   shared_ptr<list<ValueType> > items;
  };
 }
}

 实现文件如下:

#include "List.hpp"

using System::Collections::List;

template<class ValueType>
List<ValueType>::List()
{
 items = new shared_ptr<list<ValueType> >;
}

template<class ValueType>
 List<ValueType>::~List()
{}
.
.
.
template<class ValueType>
 void List<ValueType>::InsertAfter(ValueType item, ValueType value)
{
 if(!items)
 {
  throw new shared_ptr<exception>(new exception("items hadn't been initialized!"));
 }
 //程序编译时提示这儿会出错:提示信息为expect ';' before itemiterator
if((items::iterator itemiterator = find(items.begin(),items.end(),item)) != items.end())
  items.insert(itemiterator,value); 
.
.
.
后来检查全部代码,发现没有什么错误,后来到网上搜索到,应该在声明itemiterator时,在前面加上C++关键字:typename
改后的代码如下:

typename list<ValueType>::iterator itemiterator;
 itemiterator = find(items.begin(),items.end(),item);
 if(itemiterator != items.end())
  items.insert(itemiterator,value);
}

.
.
.
看来还是自己对C++不熟悉,于是从箱子底下翻出了N久以前的<<C++ Template>>
解释如下:


 关键字typename是C++标准化过程中被引入的,目的在于向编译器说明template内的某个标识符是类型(而不是其它什么东西),考虑下面的例子:
template <typename T>
   class MyClass{
          typename T::SubType * ptr;
          ...
};
在这里,第二个typename关键字的意思是:SubType是class T内部定义的一个类型,因此ptr是一个指向T::SubType类型的指针。
如果没有使用关键字typename,SubType会被认为是class T的一个static成员,于是被编译器理解为一个具体变量,从而导致以下表达式:
 T::SubType * ptr
所表达的意义变成:class T的static成员SubType与ptr的乘积。

:Visual C++ 6.0/ICL 7.1/G++ 3.2在上述的MyClass被实例化之前,都假设SubType是成员类型,因此认为上述例子是正确的,Visual C++ 7.1认为这是错误的(Visual C++ 7.1 对类型要求极其严格),然而当具体实例化时,如果SubType不是一个成员类型,四个编译器都会报错。

通常如果某个与template parameter相关的名称是个类型的时候,你就必须加上关键字typename。更详细的讨论见<<C++ Template>> p130

typename的一个典型应用是在template程序代码中使用STL容器提供的迭代器(iterators):

//basic/printcoll.hpp
#include <iostream>

//print elements in a STL container
template <typename T>
void printcoll(T const& coll)
{
     typename T::const_iterator pos;
     typename T::const_iterator end(coll.end());
    
     for (pos = coll.begin();pos != end;++pos)
     {
             std::cout << *pos << '';
     }
     std::cout << std::endl;
}

在这个function template中,coll是个STL容器,其元素类型为T。这里使用了STL容器的迭代器类型(iterator type)循环访问coll的所有元素,迭代器类型为const_iterator,每一个STL容器都有这种类型:

class stlcontainer {
       ...
       typedef ... iterator;                //可读写迭代器
       typedef ... const_iterator;   //只读迭代器
       ...
};

使用template type T的const_iterator时,你必须写出全名,并在最前面加上关键字typename:

typename T::const_iterator pos;

.template构造函数

引入关键字typename之后,又出现了一个类似的问题,考虑下面的程序代码,其中使用标准的bitset类型:

template<int N>
void printBitset(std::bitset<N> const& bs)
{
     std::cout << bs.template to_string<char,char_traits<char>,allocator<char> >();
}


/*  你也可以充分利用bitset的member typedef 像下面这样:
std::cout << bs.template to_string<std::string::value_type,
      std::string::traits_type,
      std::string::allocator_type>();*/

此例中的.template看起来有点儿怪,但是如果没有它,编译器无法知道紧跟其后的 "<" 代表的是template argument list的起始,而非是一个小于符号,注意,只有当位于“.”之前的构造取决于某个template parameter时,这个问题才会发生。以上例子中,参数bs便是取决于template parameter N.

结论是“.template”或“->template”标记只有在templates之内才能被使用,而且他们必须紧跟着“与template parameters相关的”。

我的编程环境:Ubuntu G++ boost-1_34_1

阅读更多
个人分类: C/C++/C#
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

C++中的关键字typename

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭