条款9:优先考虑别名声明而非typedefs

news/2024/4/19 15:44:12/文章来源:https://blog.csdn.net/u012069234/article/details/129252776

我相信每个人都同意使用STL容器是个好主意,并且我希望条款18能说服你让你觉得使用std:unique_ptr也是个好主意,但我猜没有人喜欢写上几次 std::unique_ptr<std::unordered_map<std::string, std::string>>这样的类型,它可能会让你患上腕管综合征的风险大大增加。

避免上述医疗悲剧也很简单,引入typedef即可:

typedefstd::unique_ptr<std::unordered_map<std::string, std::string>>UPtrMapSS; 

typedef是C++98的东西。虽然它可以在C++11中工作,但是C++11也提供了一个别名声明(alias declaration):

using UPtrMapSS =std::unique_ptr<std::unordered_map<std::string, std::string>>;

 考虑到 typedef 和声明别名具有完全一样的意义,推荐其中一个而排斥另外一个的坚实技 原因是容易令人质疑的。这样的质疑是合理的。

技术原因当然存在,但是在我提到之前。我想说的是,很多人发现使用声明别名可以使涉及到函数指针的类型的声明变得容易理解:

//FP是一个指向函数的指针的同义词,它指向的函数带有
//int和const std::string&形参,不返回任何东西
typedef void (*FP)(int, const std::string&);    //typedef//含义同上
using FP = void (*)(int, const std::string&);   //别名声明
当然,上面任何形式都不是特别让人容易下咽,并且很少有人会花费大量的时间在一个函数指针类型的标识符上,所以这很难当做选择声明别名而不是 typedef 的不可抗拒的原因。 但是,一个不可抗拒的原因是真实存在的:模板。尤其是声明别名有可能是模板化的(这种 情况下,它们被称为模板别名( alias template )),然而 typedef 这是只能说句臣妾做不 到”。模板别名给 C++11 程序员提供了一个明确的机制来表达在 C++98 中需要黑客式的 将 typedef 嵌入在模板化的 struct 中才能完成的东西。举个栗子,给一个使用个性化的分配 器 MyAlloc 的链接表定义一个标识符。使用别名模板,这就是小菜一碟:
template<typename T>                            //MyAllocList<T>是
using MyAllocList = std::list<T, MyAlloc<T>>;   //std::list<T, MyAlloc<T>>//的同义词MyAllocList<Widget> lw;                         //用户代码

使用typedef,你就只能从头开始:

template<typename T>                            //MyAllocList<T>是
struct MyAllocList {                            //std::list<T, MyAlloc<T>>typedef std::list<T, MyAlloc<T>> type;      //的同义词  
};MyAllocList<Widget>::type lw;                   //用户代码

更糟糕的是,如果你想使用在一个模板内使用typedef声明一个链表对象,而这个对象又使用了模板形参,你就不得不在typedef前面加上typename

template<typename T>
class Widget {                              //Widget<T>含有一个
private:                                    //MyAllocLIst<T>对象typename MyAllocList<T>::type list;     //作为数据成员…
}; 

这里MyAllocList<T>::type使用了一个类型,这个类型依赖于模板参数T。因此MyAllocList<T>::type是一个依赖类型(dependent type),在C++很多讨人喜欢的规则中的一个提到必须要在依赖类型名前加上typename

如果使用别名声明定义一个MyAllocList,就不需要使用typename(同时省略麻烦的“::type”后缀):

template<typename T> 
using MyAllocList = std::list<T, MyAlloc<T>>;   //同之前一样template<typename T> 
class Widget {
private:MyAllocList<T> list;                        //没有“typename”…                                           //没有“::type”
};

对你来说,MyAllocList<T>(使用了模板别名声明的版本)可能看起来和MyAllocList<T>::type(使用typedef的版本)一样都应该依赖模板参数T,但是你不是编译器。当编译器处理Widget模板时遇到MyAllocList<T>(使用模板别名声明的版本),它们知道MyAllocList<T>是一个类型名,因为MyAllocList是一个别名模板:它一定是一个类型名。因此MyAllocList<T>就是一个非依赖类型non-dependent type),就不需要也不允许使用typename修饰符。

当编译器在Widget的模板中看到MyAllocList<T>::type(使用typedef的版本),它不能确定那是一个类型的名称。因为可能存在一个MyAllocList的它们没见到的特化版本,那个版本的MyAllocList<T>::type指代了一种不是类型的东西。那听起来很不可思议,但不要责备编译器穷尽考虑所有可能。因为人确实能写出这样的代码。

举个例子,一个误入歧途的人可能写出这样的代码:

class Wine { … };template<>                          //当T是Wine
class MyAllocList<Wine> {           //特化MyAllocList
private:  enum class WineType             //参见Item10了解  { White, Red, Rose };           //"enum class"WineType type;                  //在这个类中,type是…                               //一个数据成员!
};

就像你看到的,MyAllocList<Wine>::type不是一个类型。如果Widget使用Wine实例化,在Widget模板中的MyAllocList<Wine>::type将会是一个数据成员,不是一个类型。在Widget模板内,MyAllocList<T>::type是否表示一个类型取决于T是什么,这就是为什么编译器会坚持要求你在前面加上typename

如果你尝试过模板元编程(template metaprogramming,TMP), 你一定会碰到取模板类型参数然后基于它创建另一种类型的情况。举个例子,给一个类型T,如果你想去掉T的常量修饰和引用修饰(const- or reference qualifiers),比如你想把const std::string&变成std::string。又或者你想给一个类型加上const或变为左值引用,比如把Widget变成const WidgetWidget&。(如果你没有用过模板元编程,太遗憾了,因为如果你真的想成为一个高效C++程序员,你需要至少熟悉C++在这方面的基本知识。你可以看看在条款23,27里的TMP的应用实例,包括我提到的类型转换)。

C++11在type traits(类型特性)中给了你一系列工具去实现类型转换,如果要使用这些模板请包含头文件<type_traits>。里面有许许多多type traits,也不全是类型转换的工具,也包含一些可预测接口的工具。给一个你想施加转换的类型T,结果类型就是std::transformation<T>::type,比如:

std::remove_const<T>::type          //从const T中产出T
std::remove_reference<T>::type      //从T&和T&&中产出T
std::add_lvalue_reference<T>::type  //从T中产出T&

注释仅仅简单的总结了类型转换做了什么,所以不要太随便的使用。在你的项目使用它们之前,你最好看看它们的详细说明书。

尽管写了一些,但我这里不是想给你一个关于type traits使用的教程。注意类型转换尾部的::type。如果你在一个模板内部将他们施加到类型形参上(实际代码中你也总是这么用),你也需要在它们前面加上typename。至于为什么要这么做是因为这些C++11的type traits是通过在struct内嵌套typedef来实现的。是的,它们使用类型同义词(使用typedef的做法)技术实现,而正如我之前所说这比别名声明要差。

关于为什么这么实现是有历史原因的,但是我们跳过它(我认为太无聊了),因为标准委员会没有及时认识到别名声明是更好的选择,所以直到C++14它们才提供了使用别名声明的版本。这些别名声明有一个通用形式:对于C++11的类型转换std::transformation<T>::type在C++14中变成了std::transformation_t。举个例子或许更容易理解:

 
std::remove_const<T>::type          //C++11: const T → T 
std::remove_const_t<T>              //C++14 等价形式std::remove_reference<T>::type      //C++11: T&/T&& → T 
std::remove_reference_t<T>          //C++14 等价形式std::add_lvalue_reference<T>::type  //C++11: T → T& 
std::add_lvalue_reference_t<T>      //C++14 等价形式

C++11的的形式在C++14中也有效,但是我不能理解为什么你要去用它们。就算你没办法使用C++14,使用别名模板也是小儿科。只需要C++11的语言特性,甚至每个小孩都能仿写,对吧?如果你有一份C++14标准,就更简单了,只需要复制粘贴:

template <class T> 
using remove_const_t = typename remove_const<T>::type;template <class T> 
using remove_reference_t = typename remove_reference<T>::type;template <class T> 
using add_lvalue_reference_t =typename add_lvalue_reference<T>::type; 

请记住:

  • typedef不支持模板化,但是别名声明支持。
  • 别名模板避免了使用“::type”后缀,而且在模板中使用typedef还需要在前面加上typename
  • C++14提供了C++11所有type traits转换的别名声明版本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_75214.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】数据库中锁和事务的相关知识点

1.事务的四大特点 原子性&#xff1a;事务中的所有操作要么都成功&#xff0c;要么都失败。所有的操作是一个不可分割的单位。一致性&#xff1a;一致性指的是事务执行前后&#xff0c;数据从一个合法性状态转移到另一个合法性状态。这个状态和业务有关&#xff0c;是自己定义…

Editor工具开发实用篇:EditorGUI/EditorGUILayout的区别和EditorGUILayout的方法介绍

目录 一&#xff1a;EditorGUI和EditorGUILayout区别 二&#xff1a;EditorGUILayout 1.EditorGUILayout.BeginFadeGroup(float value); 2.EditorGUILayout.BeginHorizontal EditorGUILayout.BeginVertical 3.EditorGUILayout.BeginScrollView 4.EditorGUILayout.BeginT…

sql-labs-Less1

靶场搭建好了&#xff0c;访问题目路径 http://127.0.0.1/sqli-labs-master/Less-1/ 我最开始在做sql-labs靶场的时候很迷茫&#xff0c;不知道最后到底要得到些什么&#xff0c;而现在我很清楚&#xff0c;sql注入可以获取数据库中的信息&#xff0c;而获取信息就是我们的目标…

概念+示例+横向对比+难点解析征服八大react hooks

8大hooks概念、使用场景 前言 对不同阶段的react开发者会有不同的效果&#xff0c;最终目的是能够对8大react hooks&#xff0c;完全理解&#xff0c;游刃有余。对比useState和useReducer&#xff0c;什么时候使用useMemo和useCallback&#xff0c;useEffect的参数… … use…

文献阅读笔记 # 面向大规模多版本软件系统的代码克隆检测加速技术

面向大规模多版本软件系统的代码克隆检测加速技术&#xff0c;方维康 吴毅坚 赵文耘&#xff0c;《计算机应用与软件》复旦大学软件学院、复旦大学上海市数据科学重点实验室2022 April 面向大规模多版本软件系统的代码克隆检测加速技术 摘要 很多代码克隆检测方法主要针对软…

【博学谷学习记录】超强总结,用心分享丨人工智能 多场景实战 常用英文缩写概念总结

目录PV(Page View)UV(Unique Visitor)CPM(Cost Per Mille)CPC(Cost Per Click)CPA(Cost Per Action)CPI(Cost Per Install)ACU(Average concurrent users)PCU(Peak concurrent users)ARPU(Average Revenue Per User)ARPPU(Average Revenue Per Paying User)LTV(Life Time Value…

Linux命令之lz4命令

一、lz4命令简介 LZ4是一种压缩格式&#xff0c;特点是压缩/解压缩速度超快(压缩率不如gzip)&#xff0c;如果你特别在意压缩速度&#xff0c;或者当前环境的CPU资源紧缺&#xff0c;可以考虑这种格式。lz4是一种非常快速的无损压缩算法&#xff0c;基于字节对齐LZ77系列压缩方…

西电计算机通信与网络(计网)简答题计算题核心考点汇总(期末真题+核心考点)

文章目录前言一、简答计算题真题概览二、网桥&#xff0c;交换机和路由器三、ARQ协议四、曼彻斯特编码和差分曼彻斯特编码五、CRC六、ARP协议七、LAN相关协议计算前言 主要针对西安电子科技大学《计算机通信与网络》的核心考点进行汇总&#xff0c;包含总共26章的核心简答。 【…

【Linux】Linux根文件系统扩容

场景&#xff1a;根文件系统需要至少100GB的剩余空间&#xff0c;但是目前就剩余91GB。因此&#xff0c;我们需要对根文件系统进行扩容。# df -h 文件系统 容量 已用 可用 已用% 挂载点 devtmpfs 3.9G 0 3.9G 0% /dev tmpfs …

密码暴力破解

密码的暴力破解准备工具功能简介Burp Intruder工作原理Intruder应用场景爆破实操准备工具 首先准备好BurpSuite和Dvwa作为测试工具和实验对象。 功能简介 Burp Intruder工作原理 Intruder在原始请求数据的基础上&#xff0c;通过修改各种请求参数&#xff0c;以获取不同的请…

flutter 微信聊天输入框

高仿微信聊天输入框&#xff0c;效果图如下&#xff08;目前都是静态展示&#xff0c;服务还没开始开发&#xff09;&#xff1a; 大家如果观察仔细的话 应该会发现&#xff0c;他输入框下面的高度 刚好就是 软键盘的高度&#xff1b;所以在这里就需要监听软键盘的高度。还要配…

Hbase资源隔离操作指南

1.检查集群的环境配置 1.1 HBase版本号确认> 5.11.0 引入rsgroup的Patch&#xff1a; [HBASE-6721] RegionServer Group based Assignment - ASF JIRA RegionServer Group based Assignment 社区支持版本&#xff1a;2.0.0 引入rsgroup的CDH版本 5.11.0 https://www.…

购买运动耳机应该考虑什么问题、运动达人必备的爆款运动耳机

喜欢运动的小伙伴都知道&#xff0c;运动和音乐是最配的&#xff0c;在运动中伴随着节奏感的音乐能够让自己更兴奋&#xff0c;锻炼的更加起劲儿。在运动耳机方面我也一直都有所研究&#xff0c;购买运动耳机最重要的就是要满足我们运动时候听音乐的需求&#xff0c;从佩戴舒适…

《C++ Primer Plus》(第6版)第5章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第5章编程练习《C Primer Plus》&#xff08;第6版&#xff09;第5章编程练习1. 计算闭区间内的整数和2. 重新编写程序清单5.43. 累加4. 投资价值5. 销售情况6. 销售情况27. 汽车8. 销售情况29. 销售情况210. 销售情况2《C Prim…

【技术美术图形部分】简述主流及新的抗锯齿技术

电脑的世界里没有曲线&#xff0c;都是三角面组成一个个模型的&#xff0c;因此一定会出现走样&#xff08;锯齿&#xff09;的情况&#xff0c;只是严重与否的问题&#xff0c;而AA也是实时渲染最难解决的问题之一。 Sampling&Artifacts Lecture 06 Rasterization 2 (An…

MAML算法详解(元学习)

文章目录回顾元学习MAML算法MAML和预训练模型的区别数学推导MAML实施细节总结回顾元学习 元学习的基本知识参考这篇博客元学习和机器学习的对比 MAML算法 学习初始化参数&#xff0c;所有任务的初始化的参数都是一样的 MAML和预训练模型的区别 MAML使用的是ϕ\phiϕ…

阶段十:总结专题(第六章:缓存篇)

阶段十&#xff1a;总结专题&#xff08;第六章&#xff1a;缓存篇&#xff09;Day-第六章&#xff1a;缓存篇1. Redis 数据类型**String****List****Hash****Sorted Set**2. keys 命令问题3. 过期 key 的删除策略4. Redis 持久化**AOF 持久化****AOF 重写****RDB 持久化****混…

Python 中 openpyxl 模块封装,读写 Excel 文件中自动化测试用例数据

只有测试数据和错误提示信息不同&#xff0c;其他代码都是一样的&#xff0c;不这样不易修改数据和维护&#xff0c;会有两点痛点 1.代码冗余极其严重, 程序可读性不佳 2.程序拓展性很差 往往我们在自动化测试汇总&#xff0c;会将数据放在 Excel 文件、CSV文件、数据库 Py…

Python-scatter散点图及颜色大全

# -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as pltplt.rcParams[font.sans-serif][SimHei] plt.rcParams[axes.unicode_minus] False #matplotlib画图中中文显示会有问题&#xff0c;需要这两行设置默认字体plt.xlabel(X) plt.ylabel(Y) plt.xlim…

【IP技术】ipv4和ipv6是什么?

IPv4和IPv6是两种互联网协议&#xff0c;用于在互联网上标识和寻址设备。IPv4&#xff08;Internet Protocol version 4&#xff09;是互联网协议的第四个版本&#xff0c;是当前广泛使用的互联网协议。IPv4地址由32位二进制数构成&#xff0c;通常表示为4个十进制数&#xff0…