条款42:了解typename的双重意义

2024-01-16 16:52

本文主要是介绍条款42:了解typename的双重意义,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.前言

提一个问题:以下template声明式中,class和typename有什么不同:

template<class T> class Widget//使用class
template<typename T> class Widget;//使用typename

答案是两者没有不同,当我们声明template类型参数,class和typename的意义完全相同

然而C++并不总是把class和typename视为等价,有时候一定要使用typename。为了解其时机,我们必须把template内涉及的两种名称搞清楚。

2.实例分析

假设有个template function,接收一个STL兼容容器为参数,容器内持有的对象可被赋值为ints。假设这个函数仅仅只是其第二元素值。

template<typename C>
void print2nd(const C& container)//打印容器内的第二个元素
{if(container.size()>=2){C::const_iterator iter(container.begin());//取得第一个元素的迭代器++iter;//将迭代器移往第二个元素int value=*iter;//将该元素复制到某个Intstd::cout<<value;//打印那个int}
}

在代码中特意强调两个local变量:iter和value,iter的类型是C::const_iterator,实际上是该参数必须取决于template参数C。template内出现的名称如果相依于某个template参数,称之为丛属名称(dependent names)。如果从属名称在class内呈现嵌套状,我们称它为嵌套从属名称(nest dependent name)。C::const_iterator就是这样一个名称。实际上它还是个嵌套从属类型名称(nested dependent type name),也就是个嵌套从属名称并且涉及某类型。

该函数内的另一个变量value,其类型为int,int是一个不依赖于任何template参数的名称。这样的名称是谓非从属名称(non-dependent names)。

嵌套从属名称有可能导致解析(paesing)困难,比如下面这个例子:

template<typename C>
void print2nd(const C& container)
{C::const_iterator* x;...
}

看起来好像我们声明x为一个local变量,它是个指针,指向一个C::const_iterator。但它之所以被那么认为,只因为我们已经知道C::const_iterator是个类型。如果C::const_iterator不是个类型呢?如果C有个static成员变量而碰巧被命名为const_iterator,或如果x碰巧是个global变量名称?那样的话上述代码就不再是声明一个local变量,而是一个相乘动作:C::const_iterator乘以x。

在我们知道C是什么之前,没有任何办法可以知道C::const_iterator是否为一个类型。而当编译器开始解析template print2nd时,尚未确知C是什么东西。C++有个规则可以解析(resolve)此一歧义状态:如果解析器在template中遭遇一个嵌套从属名称,它便假设这名称不是个类型,除非你告诉了它。所以缺省情况下嵌套从事名称不是类型。见下面例子:

template<typename C>
void print2nd(const C& container)
{if(container.size()>=2){C::const_iterator iter(container.begin());//该名称被假设为非类型...}}

现在应该很清楚为什么这不是有效的C++代码了。iter声明式只有在C::const_iterator是个类型时才合理,但我们并没有告诉C++所它是,于是C++假设它不是。若要矫正这个形势,我们必须告诉C++说C::const_iterator是个类型。只要紧邻它之前放置关键字typename即可:

template<typename C>//合法的C++代码
void print2nd(const C& container)
{if(container.size() >2){typename C::const_iterator iter(container.begin());....}
}

一般性规则很简单:任何时候当你想在template中涉及一个嵌套从属类型名称,就必须在紧邻它的前一个位置加上关键字typename。

typename只被用来验明嵌套从属类型名称;其它名称不该有它的存在。比如下面的function template,接受一个容器和一个“指向该容器”的迭代器:

template<typename C>//允许使用typename
void f(const C& container,typename C::iterator iter);//不允许使用typename,一定要使用typename

上述的C并不是嵌套从事类型名称,所以container时并不需要以typename为前导,但C::iterator是个嵌套从属类型名称,所以必须以typename为前导。

“typename”必须作为嵌套从事类型名称的前缀词,typename不可以出现在base classes list内的嵌套从属类型名称之前,也不可在member initialization(成员初值列)中作为base classes修饰符。比如:

template<typename T>
class Derived:public Base<T>::Nested{//base class list中不允许“typename”public:explicit Derived(int x):Base<T>::Nested(x)//成员初值列表中不允许typename{typename Base<T>::Nested temp;//嵌套从属类型名称....//即不在base class list中,也不在成员初值列表中,作为一个base class修饰符需加        上typename}....
};

这样的不一致性令人烦恼。

最后看一个typename例子,那是在程序中经常看到的代表性例子。假设我们正在编写一个function template,它接受一个迭代器,而我们打算为该迭代器涉及的对象做一份local副本temp。我们可以这样写:

template<typename IterT>
void workWithIterator(IterT iter)
{typename std::iterator_traits<IterT>::value_type temp(*iter);....
}

该语句声明一个local变量(temp),使用IterT对象所指物的相同类型,并将temp初始化为iter所指物。如果IterT是个vector<int>::iterator,temp的类型就是string。由于std::iterator_traits<IterT>::value_type是个嵌套从事类型名称,所以我们必须在它之前放置typename

如果你认为std::iterator_traits<IterT>::value_type读起来不畅快,那么可以建立一个typedef。对于traits成员名称如value_type。普遍的习惯是设定typedef名称用以代表某个traits成员名称,于是常常可以看到类似这样的local  typedef:

template<typename IterT>
void workWithIterator(IterT iter)
{typedef typename std::iterator_traits<IterT>::value_type  value_type;value_type temp(*iter);....
}

3.总结

(1)声明template参数时,前缀关键字class和typename可互换;

(2)请使用关键字typename标识嵌套从事类型名称;但不得在base class lists(基类)或member initialization list(成员初值列表)内以它作为base class修饰符。

这篇关于条款42:了解typename的双重意义的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/613281

相关文章

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

速了解MySQL 数据库不同存储引擎

快速了解MySQL 数据库不同存储引擎 MySQL 提供了多种存储引擎,每种存储引擎都有其特定的特性和适用场景。了解这些存储引擎的特性,有助于在设计数据库时做出合理的选择。以下是 MySQL 中几种常用存储引擎的详细介绍。 1. InnoDB 特点: 事务支持:InnoDB 是一个支持 ACID(原子性、一致性、隔离性、持久性)事务的存储引擎。行级锁:使用行级锁来提高并发性,减少锁竞争

UVM:callback机制的意义和用法

1. 作用         Callback机制在UVM验证平台,最大用处就是为了提高验证平台的可重用性。在不创建复杂的OOP层次结构前提下,针对组件中的某些行为,在其之前后之后,内置一些函数,增加或者修改UVM组件的操作,增加新的功能,从而实现一个环境多个用例。此外还可以通过Callback机制构建异常的测试用例。 2. 使用步骤         (1)在UVM组件中内嵌callback函

PHP: 深入了解一致性哈希

前言 随着memcache、redis以及其它一些内存K/V数据库的流行,一致性哈希也越来越被开发者所了解。因为这些内存K/V数据库大多不提供分布式支持(本文以redis为例),所以如果要提供多台redis server来提供服务的话,就需要解决如何将数据分散到redis server,并且在增减redis server时如何最大化的不令数据重新分布,这将是本文讨论的范畴。 取模算法 取模运

Weex入门教程之1,了解Weex

【资料合集】Weex Conf回顾集锦:讲义PDF+活动视频! PDF分享:链接:http://pan.baidu.com/s/1hr8RniG 密码:fa3j 官方教程:https://weex-project.io/cn/v-0.10/guide/index.html 用意 主要是介绍Weex,并未涉及开发方面,好让我们开始开发之前充分地了解Weex到底是个什么。 以下描述主要摘取于

Java了解相对较多!

我是对Java了解相对较多,而对C#则是因工作需要才去看了一下,C#跟Java在语法上非常相似,而最初让我比较困惑的就是委托、事件部分,相信大多数初学者也有类似的困惑。经过跟Java的对比学习,发现这其实跟Java的监听、事件是等同的,只是表述上不同罢了。   委托+事件是观察者模式的一个典型例子,所谓的委托其实就是观察者,它会关心某种事件,一旦这种事件被触发,这个观察者就会行动。   下

使用WebP解决网站加载速度问题,这些细节你需要了解

说到网页的图片格式,大家最常想到的可能是JPEG、PNG,毕竟这些老牌格式陪伴我们这么多年。然而,近几年,有一个格式悄悄崭露头角,那就是WebP。很多人可能听说过,但到底它好在哪?你的网站或者项目是不是也应该用WebP呢?别着急,今天咱们就来好好聊聊WebP这个图片格式的前世今生,以及它值不值得你花时间去用。 为什么会有WebP? 你有没有遇到过这样的情况?网页加载特别慢,尤其是那

初步了解VTK装配体

VTK还不太了解,根据资料, vtk.vtkAssembly 是 VTK库中的一个重要类,允许通过将多个vtkActor对象组合在一起来创建复杂的3D模型。 import vtkimport mathfrom vtk.util.colors import *filenames = ["cylinder.stl","sphere.stl","torus.stl"]dt = 1.0renW

数据库系统 第42节 数据库索引简介

数据库索引是数据库表中一个或多个列的数据结构,用于加快数据检索速度。除了基础的B-Tree索引,其他类型的索引针对特定的数据类型和查询模式提供了优化。以下是几种不同类型的索引及其使用场景的详细说明和示例代码。 1. 位图索引 (Bitmap Index) 位图索引适用于具有少量不同值的列(例如性别、国家代码等),它使用位图来表示数据,从而提高查询效率。 适用场景:当列中的值域较小,且数据分布