C++中typename关键字的使用方法和注意事项
2016-06-11 18:52
701 查看
C++中typename关键字的使用方法和注意事项
1, 什么地方使用?用在模板定义里,标明其后的模板参数是类型参数。例如
template<typename T, typename Y> T foo(const T& t, const Y& y){//....}; templace<typename T> class CTest { private: T t; public: //... } |
什么原因呢?请看第二条,也就是typename的第二个用法。
2, 模板中标明“内嵌依赖类型名”
这里有三个词,内嵌、依赖、类型名。那么什么是“内嵌依赖类型名(nested dependent type name)”?
请看SGI STL里的一个例子, 只是STL中count范型算法的实现:
template <class _InputIter, class _Tp> typename iterator_traits<_InputIter>::difference_type count(_InputIter __first, _InputIter __last, const _Tp& __value) { __STL_REQUIRES(_InputIter, _InputIterator); __STL_REQUIRES(typename iterator_traits<_InputIter>::value_type, _EqualityComparable); __STL_REQUIRES(_Tp, _EqualityComparable); typename iterator_traits<_InputIter>::difference_type __n = 0; for ( ; __first != __last; ++__first) if (*__first == __value) ++__n; return __n; } |
typename iterator_traits<_InputIter>::difference_type
typename iterator_traits<_InputIter>::value_type
typename iterator_traits<_InputIter>::difference_type __n = 0;
difference_type, value_type就是依赖于_InputIter(模板类型参数)的类型名。源码如下:
template <class _Iterator> struct iterator_traits { typedef typename _Iterator::iterator_category iterator_category; typedef typename _Iterator::value_type value_type; typedef typename _Iterator::difference_type difference_type; typedef typename _Iterator::pointer pointer; typedef typename _Iterator::reference reference; }; |
依赖是指依赖于一个模板参数。typename iterator_traits<_InputIter>::difference_type中difference_type依赖于模板参数_InputIter.
类型名是指这里最终要指出的是个类型名,而不是变量。例如iterator_traits<_InputIter>::difference_type完全有可能是类iterator_traits<_InputIter>类里的一个static对
象。而且当我们这样写的时候,C++默认就是解释为一个变量的。所以,为了和变量区分,必须使用typename告诉编译器。
那么是不是所有的T::type_or_variable, 或者tmpl<T>:type_or_variable都需要使用typename呢?不是,有以下两个例外。
3 例外
(1)类模板定义中的基类列表。
例如
template<class T> class Derived: public Base<T>::XXX { ... } |
Derived(int x) : Base<T>::xxx(x) { ... } |
(1)基类列表里肯定是类型名,(2)初始化列表里肯定是成员变量名。
参考:
typename c++ 百度
=================================================
C++箴言:理解typename的两个含义
问题:在下面的 template declarations(模板声明)中 class 和 typename 有什么不同? template<class T> class Widget; // uses "class" template<typename T> class Widget; // uses "typename" |
的观点看,class 和 typename 在声明一个 template parameter(模板参数)时意味着完全相同的东西。
然而,C++ 并不总是把 class 和 typename 视为等同的东西。有时你必须使用 typename。为了理解这一点,我们不得不讨论你会在一个 template(模板)中涉及到的两种名字。
假设我们有一个函数的模板,它能取得一个 STL-compatible container(STL 兼容容器)中持有的能赋值给 ints 的对象。进一步假设这个函数只是简单地打印它的第二个元素的值。它是一个用糊涂的方法实现的糊涂的函数,而且就像我下面写的,它甚至不能编译,但是请将这些事先放在一边——有一种方法能发现我的愚蠢:
template<typename C> // print 2nd element in void print2nd(const C& container) // container; { // this is not valid C++! if (container.size() >= 2) { C::const_iterator iter(container.begin()); // get iterator to 1st element ++iter; // move iter to 2nd element int value = *iter; // copy that element to an int std::cout << value; // print the int } } |
是一个 nested dependent name(嵌套依赖名字)。实际上,它是一个 nested dependent type name(嵌套依赖类型名),也就是说,一个涉及到一个 type(类型)的 nested dependent name(嵌套依赖名字)。
print2nd 中的另一个 local variable(局部变量)value 具有 int 类型。int 是一个不依赖于任何 template parameter(模板参数)的名字。这样的名字以 non-dependent names(非依赖名字)闻名。(我想不通为什么他们不称它为 independent names(无依赖名字)。如果,像我一样,你发现术语 "non-dependent" 是一个令人厌恶的东西,你就和我产生了共鸣,但是 "non-dependent" 就是这类名字的术语,所以,像我一样,转转眼睛放弃你的自我主张。)
nested dependent name(嵌套依赖名字)会导致解析困难。例如,假设我们更加愚蠢地以这种方法开始 print2nd:
template<typename C> void print2nd(const C& container) { C::const_iterator * x; ... } |
local variable(局部变量),而是成为 C::const_iterator 乘以 x!当然,这听起来有些愚蠢,但它是可能的,而编写 C++ 解析器的人必须考虑所有可能的输入,甚至是愚蠢的。
直到 C 成为已知之前,没有任何办法知道 C::const_iterator 到底是不是一个 type(类型),而当 template(模板)print2nd 被解析的时候,C 还不是已知的。C++ 有一条规则解决这个歧义:如果解析器在一个 template(模板)中遇到一个 nested dependent name(嵌套依赖名字),它假定那个名字不是一个 type(类型),除非你用其它方式告诉它。缺省情况下,nested dependent name(嵌套依赖名字)不是 types(类型)。(对于这条规则有一个例外,我待会儿告诉你。)
记住这个,再看看 print2nd 的开头:
template<typename C> void print2nd(const C& container) { if (container.size() >= 2) { C::const_iterator iter(container.begin()); // this name is assumed to ... // not be a type |
template<typename C> // this is valid C++ void print2nd(const C& container) { if (container.size() >= 2) { typename C::const_iterator iter(container.begin()); ... } } |
放在紧挨着它的前面。(重申一下,我待会儿要描述一个例外。)
typename 应该仅仅被用于标识 nested dependent type name(嵌套依赖类型名);其它名字不应该用它。例如,这是一个取得一个 container(容器)和这个 container(容器)中的一个 iterator(迭代器)的 function template(函数模板):
template<typename C> // typename allowed (as is "class") void f(const C& container, // typename not allowed typename C::iterator iter); // typename required |
"typename must precede nested dependent type names"(“typename 必须前置于嵌套依赖类型名”)规则的例外是 typename 不必前置于在一个 list of base classes(基类列表)中的或者在一个 member initialization list(成员初始化列表)中作为一个 base classes identifier(基类标识符)的 nested dependent type name(嵌套依赖类型名)。例如:
template<typename T> class Derived: public Base<T>::Nested { // base class list: typename not public: // allowed explicit Derived(int x) : Base<T>::Nested(x) // base class identifier in mem { // init. list: typename not allowed typename Base<T>::Nested temp; // use of nested dependent type ... // name not in a base class list or } // as a base class identifier in a ... // mem. init. list: typename required }; |
让我们来看最后一个 typename 的例子,因为它在你看到的真实代码中具有代表性。假设我们在写一个取得一个 iterator(迭代器)的 function template(函数模板),而且我们要做一个 iterator(迭代器)指向的 object(对象)的局部拷贝 temp,我们可以这样做:
template<typename IterT> void workWithIterator(IterT iter) { typename std::iterator_traits<IterT>::value_type temp(*iter); ... } |
的说法就是 "the type of thing pointed to by objects of type IterT"(“被类型为 IterT 的对象所指向的东西的类型”)。这个语句声明了一个与 IterT objects 所指向的东西类型相同的 local variable(局部变量)(temp),而且用 iter 所指向的 object(对象)对 temp 进行了初始化。如果 IterT 是 vector<int>::iterator,temp 就是 int 类型。如果 IterT 是 list<string>::iterator,temp
就是 string 类型。因为 std::iterator_traits<IterT>::value_type 是一个 nested dependent type name(嵌套依赖类型名)(value_type 嵌套在 iterator_traits<IterT> 内部,而且 IterT 是一个 template parameter(模板参数)),我们必须让它被 typename 前置。
如果你觉得读 std::iterator_traits<IterT>::value_type 令人讨厌,就想象那个与它相同的东西来代表它。如果你像大多数程序员,对多次输入它感到恐惧,那么你就需要创建一个 typedef。对于像 value_type 这样的 traits member names(特性成员名),一个通用的惯例是 typedef name 与 traits member name 相同,所以这样的一个 local typedef 通常定义成这样:
template<typename IterT> void workWithIterator(IterT iter) { typedef typename std::iterator_traits<IterT>::value_type value_type; value_type temp(*iter); ... } |
作为结束语,我应该提及编译器与编译器之间对围绕 typename 的规则的执行情况的不同。一些编译器接受必需 typename 时它却缺失的代码;一些编译器接受不许 typename 时它却存在的代码;还有少数的(通常是老旧的)会拒绝 typename 出现在它必需出现的地方。这就意味着 typename 和 nested dependent type names(嵌套依赖类型名)的交互作用会导致一些轻微的可移植性问题。
Things to Remember
·在声明 template parameters(模板参数)时,class 和 typename 是可互换的。
·用 typename 去标识 nested dependent type names(嵌套依赖类型名),在 base class lists(基类列表)中或在一个 member initialization list(成员初始化列表)中作为一个 base class identifier(基类标识符)时除外。
相关文章推荐
- c++单链表【构造函数、运算符重载、析构函数、增删查改等】
- c++双链表【构造函数、运算符重载、析构函数、增删查改及逆置等】
- c++顺序表【构造函数、运算符重载、析构函数、增删查改等】
- C++用模板实现顺序表和栈
- C++用模板实现双链表和队列
- 【C++】智能指针shared_ptr 定位删除器(仿函数)
- 浅析c++异常
- C++之虚继承
- C语言 在屏幕上输出菱形图案
- C语言 求出100~999之间的所有“水仙花数”并输出
- C语言:编写strcpy函数
- C语言打印100 ——200之间的素数
- C语言判断1000—2000年之间的闰年
- C语言中不允许创建临时变量,交换两个数的内容
- C语言中写一个函数返回参数二进制中 1 的个数
- C语言求两个数中最大公约数
- 第14周项目3--数组类模板
- T2:滑雪(cowski.pas/c/cpp)
- notepad++搭建C语言环境(Dev c++)
- C语言动态和静态内存分配