![](https://img-blog.csdnimg.cn/20201014180756757.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
EffectiveModernCppChinese
文章平均质量分 95
对C++经典书籍的解读
学IT的细胞膜
记录学习编程之路
展开
-
item40 对于并发使用`std::atomic`,对于特殊内存使用`volatile`
但是它们通常做出一些没有数据竞争的程序中才有效的优化,这些优化在存在数据竞争的程序中会造成异常和不可预测的行为。但是在编译器拿到看起来合理的代码,执行了模板实例化,内联和一系列重排序优化之后,结果会出现冗余访问和无用存储,所以编译器需要摆脱这样的情况并不少见。)这意味对我们的代码,对象被构建,在其上的操作表现得像操作是在互斥锁保护的关键区内,但是通常这些操作是使用特定的机器指令实现,这比锁的实现更高效。那些不那么相同,但是如果我们暂时忽略它,只关注编译器执行的操作,则概念上可以说,编译器看到这个,原创 2024-07-08 10:13:34 · 691 阅读 · 0 评论 -
item39 对于一次性事件通信考虑使用`void`的*futures*
互斥锁被用于保护共享数据的访问,但是可能检测任务和反应任务可能不会同时访问共享数据,比如说,检测任务会初始化一个全局数据结构,然后给反应任务用,如果检测任务在初始化之后不会再访问这个数据结构,而在检测任务表明数据结构准备完了之前反应任务不会访问这个数据结构,这两个任务在程序逻辑下互不干扰,也就没有必要使用互斥锁。不好的一点是反应任务中轮询的开销。这样,反应线程占用了可能能给另一个任务使用的硬件线程,每次启动或者完成它的时间片都增加了上下文切换的开销,并且保持核心一直在运行状态,否则的话本来可以停下来省电。原创 2024-07-08 10:12:56 · 912 阅读 · 0 评论 -
item38 关注不同线程句柄的析构行为
future。原创 2024-07-08 10:12:14 · 596 阅读 · 0 评论 -
item37 使`std::thread`在所有路径最后都不可结合
每个可结合的joinable)或者不可结合的unjoinable可结合状态的对应于正在运行或者可能要运行的异步执行线程。比如,对应于一个阻塞的(blocked)或者等待调度的线程的是可结合的,对应于运行结束的线程的也可以认为是可结合的。不可结合的正如所期待:一个不是可结合状态的。不可结合的默认构造的s。这种没有函数执行,因此没有对应到底层执行线程上。已经被移动走的对象。移动的结果就是一个原来对应的执行线程现在对应于另一个。已经被join的。在join之后,不再对应于已经运行完了的执行线程。原创 2024-07-08 10:11:35 · 684 阅读 · 0 评论 -
item36 如果有异步的必要请指定`std::launch::async`
那些是使机器资源超额或者线程耗尽的条件,此时任务推迟执行才最有可能发生。毕竟,如果硬件没有资源耗尽,没有理由不安排任务并发执行。的默认启动策略——你不显式指定一个策略时它使用的那个——不是上面中任意一个。作为启动策略的工具,拥有它会非常方便,而且编写起来很容易也使它看起来很棒。和标准库的线程管理组件承担线程创建和销毁的责任,避免资源超额,以及平衡负载。执行函数时(或者其他可调用对象),你通常希望异步执行函数。不幸的是,没有直接的方法来查看。的循环使用超时机制,因为在一个延时的任务(参见。原创 2024-07-08 10:10:50 · 544 阅读 · 0 评论 -
Item35 优先考虑基于任务的编程而非基于线程的编程
C++11的伟大成功之一是将并发整合到语言和库中。熟悉其他线程API(比如pthreads或者Windows threads)的开发者有时可能会对C++提供的斯巴达式(译者注:应该是简陋和严谨的意思)功能集感到惊讶,这是因为C++对于并发的大量支持是在对编译器作者约束的层面。由此产生的语言保证意味着在C++的历史中,开发者首次通过标准库可以写出跨平台的多线程程序。这为构建表达库奠定了坚实的基础,标准库并发组件(任务tasks,期望futures,线程threads,互斥mutexes。原创 2024-07-08 10:10:06 · 208 阅读 · 0 评论 -
item34 考虑*lambda*而非`std::bind`
无论如何,这样的理解都是值得的,因为你永远不知道何时会在阅读或维护的代码库中遇到。这是可以理解的,但是在这种情况下,改变是更好的,因为在C++11中,在C++14中,通常可以省略标准运算符模板的模板类型实参,因此无需在此处提供。进一步假设,在程序的某个时刻,我们已经确定需要设置一个小时后响30秒的警报器。(答案是传递给bind对象的所有实参都是通过引用传递的,因为此类对象的函数调用运算符使用完美转发。)等简化在C++14中的代码,其中标准后缀基于C++11对用户自定义常量的支持。的调用是通过一个函数指针。原创 2024-07-07 14:45:04 · 594 阅读 · 0 评论 -
item33 对`auto&&`形参使用`decltype`以`std::forward`它们
解释过如果一个左值实参被传给通用引用的形参,那么形参类型会变成左值引用。传递的是右值,形参就会变成右值引用。时,惯例决定了类型实参是左值引用时来表明要传进左值,类型实参是非引用就表明要传进右值。是非传统的,不过它产生的实例化结果与传统类型相同。一般来说,当你在使用完美转发时,你是在一个接受类型参数为。这个特性的实现是非常直截了当的:即在闭包类中的。的类型来确定传递进来的实参是一个左值还是右值,如果传递的是一个右值,指定为右值引用,这会发生什么。就能产生一个左值引用。就会产生右值引用,而不是常规的非引用。原创 2024-07-07 14:43:59 · 818 阅读 · 0 评论 -
item32 使用初始化捕获来移动对象到闭包中
如果你要复制的对象复制开销非常高,但移动的成本却不高(例如标准库中的大多数容器),并且你希望的是宁愿移动该对象到闭包而不是复制它。表达式要多,但这并不难改变这样一个事实,即如果你希望使用一个C++11的类来支持其数据成员的移动初始化,那么你唯一要做的就是在键盘上多花点时间。这清楚地表明了,这个C++14的捕获概念是从C++11发展出来的的,在C++11中,无法捕获表达式的结果。这种移动构造是模仿移动捕获的关键,因为将右值移动到bind对象是我们解决无法将右值移动到C++11闭包中的方法。原创 2024-07-07 14:43:08 · 623 阅读 · 0 评论 -
item31 避免使用默认捕获模式
CHAPTER 6 Lambda Expressionslambda表达式是C++编程中的游戏规则改变者。这有点令人惊讶,因为它没有给语言带来新的表达能力。lambda可以做的所有事情都可以通过其他方式完成。但是lambda是创建函数对象相当便捷的一种方法,对于日常的C++开发影响是巨大的。没有lambda时,STL中的“”算法(比如,,,等)通常需要繁琐的谓词,但是当有lambda可用时,这些算法使用起来就变得相当方便。用比较函数(比如,,,等)来自定义算法也是同样方便的。在STL外,lambda可以快速原创 2024-07-07 14:42:28 · 483 阅读 · 0 评论 -
item30 熟悉完美转发失败的情况
在大多数情况下,完美转发工作的很好。你基本不用考虑其他问题。但是当其不工作时——当看起来合理的代码无法编译,或者更糟的是,虽能编译但无法按照预期运行时——了解完美转发的缺陷就很重要了。同样重要的是如何解决它们。在大多数情况下,都很简单。当模板类型推导失败或者推导出错误类型,完美转发会失败。导致完美转发失败的实参种类有花括号初始化,作为空指针的0或者NULL,仅有声明的整型数据成员,模板和重载函数的名字,位域。原创 2024-07-06 15:27:15 · 836 阅读 · 0 评论 -
item29 假定移动操作不存在,成本高,未被使用
为了升级到C++11,C++98的很多标准库做了大修改,为很多类型提供了移动的能力,这些类型的移动实现比复制操作更快,并且对库的组件实现修改以利用移动操作。移动语义确实是这样重要的特性。解释了原因,标准库中的某些容器操作提供了强大的异常安全保证,确保依赖那些保证的C++98的代码在升级到C++11且仅当移动操作不会抛出异常,从而可能替换操作时,不会不可运行。的移动操作还是复制操作都将花费线性时间的开销,因为每个容器中的元素终归需要拷贝或移动一次,这与“移动一个容器就像操作几个指针一样方便”的含义相去甚远。原创 2024-07-06 15:26:10 · 842 阅读 · 0 评论 -
item28 理解引用折叠
存在两种类型的引用(左值和右值),所以有四种可能的引用组合(左值的左值,左值的右值,右值的右值,右值的左值)。如果一个上下文中允许引用的引用存在(比如,模板的实例化),引用根据规则。通用引用的概念是有用的,因为它使你不必一定意识到引用折叠的存在,从直觉上推导左值和右值的不同类型,在凭直觉把推导的类型代入到它们出现的上下文中之后应用引用折叠规则。和通用引用之前,必须明确在C++中引用的引用是非法的。会产生对左值引用的右值引用,然后引用折叠规则告诉我们结果就是左值引用。第一,也是最常见的就是模板实例化。原创 2024-07-06 15:25:17 · 740 阅读 · 0 评论 -
item27 熟悉通用引用重载的替代方法
这个条款探讨了几种,通过避免在通用引用上重载的设计,或者通过限制通用引用可以匹配的参数类型,来实现所期望行为的方法。——是重载的,一个接受通用引用参数,但是重载规则不仅依赖通用引用形参,还依赖新引入的标签形参,标签值设计来保证有不超过一个的重载是合适的匹配。构造函数),但是更复杂的系统中,在最终到达判断实参类型是否可接受的地方之前,通用引用会被多层函数调用转发。因为使用了完美转发,所以具有最大效率,因为控制了通用引用与重载的结合而不是禁止它,这种技术可以被用于不可避免要用重载的情况(比如构造函数)。原创 2024-07-06 15:24:11 · 525 阅读 · 0 评论 -
item26 避免在通用引用上重载
中说明,在适当的条件下,C++会生成拷贝和移动构造函数,即使类包含了模板化的构造函数,模板函数能实例化产生与拷贝和移动构造函数一样的签名,也在合适的条件范围内。如同注释表示的,派生类的拷贝和移动构造函数没有调用基类的拷贝和移动构造函数,而是调用了基类的完美转发构造函数!根据正常的重载解决规则,精确匹配优先于类型提升的匹配,所以被调用的是通用引用的重载。确实我们是这样想的,但是编译器严格遵循C++的规则,这里的相关规则就是控制对重载函数调用的解析规则。使用通用引用的函数在C++中是最贪婪的函数。原创 2024-07-06 15:23:13 · 539 阅读 · 0 评论 -
item25 对右值引用使用`std::move`,对通用引用使用`std::forward`
对标准中教条的(也可以说是有毒的)絮叨做些解释,这个特定的好事就是说,编译器可能会在按值返回的函数中消除对局部对象的拷贝(或者移动),如果满足(1)局部对象与函数返回值的类型相同;增加的开销根据实现不同而不同,这些开销是否值得担心也跟应用和库的不同而有所不同,但是事实上,将通用引用模板替换成对左值引用和右值引用的一对函数重载在某些情况下会导致运行时的开销。一些人将RVO的应用区分为命名的和未命名的(即临时的)局部对象,限制了RVO术语应用到未命名对象上,并把对命名对象的应用称为。而是设计的可扩展性差。原创 2024-07-06 15:22:01 · 531 阅读 · 0 评论 -
item24 区分通用引用与右值引用
如果初始值是一个右值,那么通用引用就会是对应的右值引用,如果初始值是一个左值,那么通用引用就会是一个左值引用。,它们依赖于右值引用和通用引用的区别。就像牛顿的力学定律(本质上不正确),比起爱因斯坦的广义相对论(这是真相)而言,往往更简单,更易用。”),并且,当你在和你的合作者交流时,它会帮助你避免歧义(“在这里我在用一个通用引用,而非右值引用”)。它们的二重性使它们既可以绑定到右值上(就像右值引用),也可以绑定到左值上(就像左值引用)。”的另一种意思是,它既可以是右值引用,也可以是左值引用。原创 2024-07-06 15:20:22 · 958 阅读 · 0 评论 -
item23 理解`std::move`和`std::forward`
当你第一次了解到移动语义()和完美转发(使编译器有可能用廉价的移动操作来代替昂贵的拷贝操作。正如拷贝构造函数和拷贝赋值操作符给了你控制拷贝语义的权力,移动构造函数和移动赋值操作符也给了你控制移动语义的权力。移动语义也允许创建只可移动(move-only)的类型,例如和。使接收任意数量实参的函数模板成为可能,它可以将实参转发到其他的函数,使目标函数接收到的实参与被传递给转发函数的实参保持一致。是连接这两个截然不同的概念的胶合剂。它是使移动语义和完美转发变得可能的基础语言机制。原创 2024-07-06 15:18:50 · 623 阅读 · 0 评论 -
item22 当使用Pimpl惯用法,请在实现文件中定义特殊成员函数
如果你曾经与过多的编译次数斗争过,你会对Pimpl惯用法很熟悉。凭借这样一种技巧,你可以将类数据成员替换成一个指向包含具体实现的类(或结构体)的指针,并将放在主类(primary class)的数据成员们移动到实现类(implementation class)去,而这些数据成员的访问将通过指针间接访问。举个例子,假如有一个类Widgetclass Widget() { //定义在头文件“widget.h”public:Widget();private://Gadget是用户自定义的类型。原创 2024-07-05 15:03:38 · 919 阅读 · 0 评论 -
item21 优先考虑使用`std::make_unique`和`std::make_shared`,而非直接使用`new`
源代码中的重复增加了编译的时间,会导致目标代码冗余,并且通常会让代码库使用更加困难。但是,因为控制块和对象被放在同一块分配的内存块中,直到控制块的内存也被销毁,对象占用的内存才被释放。这种优化减少了程序的静态大小,因为代码只包含一个内存分配调用,并且它提高了可执行代码的速度,因为内存只分配一次。这些函数的存在意味着对这些类型的对象的全局内存分配和释放是不合常规的。中的两个:接收任意的多参数集合,完美转发到构造函数去动态分配一个对象,然后返回这个指向这个对象的指针。函数中,完美转发使用小括号,而不是花括号。原创 2024-07-05 15:02:49 · 811 阅读 · 0 评论 -
item20 当`std::shared_ptr`可能悬空时使用`std::weak_ptr`
此模式的主要组件是subjects(状态可能会更改的对象)和observers(状态发生更改时要通知的对象)。调用者应该接收缓存对象的智能指针,调用者也应该确定这些对象的生命周期,但是缓存本身也需要一个指针指向它所缓存的对象。缓存对象的指针需要知道它是否已经悬空,因为当工厂客户端使用完工厂产生的对象后,对象将被销毁,关联的缓存条目会悬空。当然,不是所有的使用指针的数据结构都是严格分层的,所以当发生这种情况时,比如上面所述缓存和观察者列表的实现之类的,知道。)的但是不参与资源所有权共享的指针是很方便的。原创 2024-07-05 15:02:07 · 378 阅读 · 0 评论 -
item19 对于共享资源使用`std::shared_ptr`
执行需要原子引用计数修改的操作需要承担一两个原子操作开销,这些操作通常都会一一映射到机器指令上,所以即使对比非原子指令来说,原子指令开销较大,但是它们仍然只是单个指令上的。对象的指针,它是未定义行为的Game, Set, and Match(译注:一部关于网球的电影,但是译者没看过。的构造函数一个原始指针,它会为指向的对象创建一个控制块(因此有个引用计数值)。作为这些轻微开销的交换,你得到了动态分配的资源的生命周期自动管理的好处。就垃圾回收来说,客户端不需要关心指向对象的生命周期,而对象的析构是确定性的。原创 2024-07-04 09:29:26 · 380 阅读 · 0 评论 -
item18 对于独占资源使用`std::unique_ptr`
当资源需要销毁时可调用的任意函数(或者函数对象,包括lambda表达式)。如果通过。原创 2024-07-04 09:27:03 · 215 阅读 · 0 评论 -
item17 理解特殊成员函数的生成
它来源于长期的观察,即用户接管拷贝操作的需求几乎都是因为该类会做其他资源的管理,这也几乎意味着(1)无论哪种资源管理如果在一个拷贝操作内完成,也应该在另一个拷贝操作内完成(2)类的析构函数也需要参与资源的管理(通常是释放)。)(译注:禁用的是自动生成的拷贝操作,对于用户声明的拷贝操作不受影响)毕竟,如果逐成员移动对该类来说不合适,也没有理由指望逐成员拷贝操作是合适的。规则背后的解释依然有效,再加上对声明拷贝操作阻止移动操作隐式生成的观察,使得C++11不会为那些有用户定义的析构函数的类生成移动操作。原创 2024-07-03 15:04:49 · 302 阅读 · 0 评论 -
item16 让`const`成员函数线程安全
在这个类中,使用一个函数来计算多项式的根是很有用的,也就是多项式的值为零的时候(译者注:通常也被叫做零点,即使得多项式值为零的那些取值)。计算多项式的根是很复杂的,因此如果不需要的话,我们就不做。在没有同步的情况下,让多个线程执行读操作是安全的。声明在C++11中与在C++98中一样正确(检索多项式的根并不会更改多项式的值),因此需要纠正的是线程安全的缺乏。这就意味着在没有同步的情况下,这些代码会有不同的线程读写相同的内存,这就是数据竞争(的经典使用样例,这也是为什么它是数据成员声明的一部分。原创 2024-07-03 15:03:56 · 332 阅读 · 0 评论 -
item15 尽可能的使用`constexpr`
(技术上来讲,它们的值在翻译期(translation)决议,所谓翻译不仅仅包含是编译(compilation)也包含链接(linking),除非你准备写C++的编译器和链接器,否则这些对你不会造成影响,所以你编程时无需担心,把这些。)它也意味着以前相对严格的编译期完成的工作和运行时完成的工作的界限变得模糊,一些传统上在运行时的计算过程能并入编译时。如果你想编译器保证一个变量有一个值,这个值可以放到那些需要编译期常量(compile-time constants)的上下文的地方,你需要的工具是。原创 2024-07-03 10:37:14 · 912 阅读 · 0 评论 -
item14 如果函数不抛出异常请使用`noexcept`
违反了前置条件,导致了未定义行为。原创 2024-07-03 10:33:09 · 706 阅读 · 0 评论 -
item13 优先考虑`const_iterator`而非`iterator`
支持不足(译注:C++14支持但是C++11的时候还没)的情况是:当你想写最大程度通用的库,并且这些库代码为一些容器和类似容器的数据结构提供。如果你使用C++11,并且想写一个最大程度通用的代码,而你使用的STL没有提供缺失的非成员函数。首先不容易创建它们,其次就算你有了它,它的使用也是受限的。提到的别名声明,因为这段代码在演示C++98做法,别名声明是C++11加入的特性)上面的说法对C++11和C++98都是正确的,但是在C++98中,标准库对。(它不是C++98的限制,也不是C++11的限制,只是。原创 2024-07-03 09:50:00 · 537 阅读 · 0 评论 -
item12 使用`override`声明重写函数
比如,下面的代码是完全合法的,咋一看,还很有道理,但是它没有任何虚函数重写——没有一个派生类函数联系到基类函数。后面我还会提到引用限定符修饰成员函数,但是现在,只需要记住如果基类的虚函数有引用限定符,派生类的重写就必须具有相同的引用限定符。,你可以只改变基类函数签名,重编译系统,再看看你造成了多大的问题(即,多少派生类不能通过编译),然后决定是否值得如此麻烦更改函数签名。,你只能寄希望于完善的单元测试,因为,正如我们所见,派生类虚函数本想重写基类,但是没有,编译器也没有探测并发出诊断信息。原创 2024-07-03 09:48:43 · 513 阅读 · 0 评论 -
item11 优先考虑使用*deleted*函数而非使用未定义的私有声明
如果你写的代码要被其他人使用,你不想让他们调用某个特殊的函数,你通常不会声明这个函数。无声明,不函数。简简单单!但有时C++会给你自动声明一些函数,如果你想防止客户调用这些函数,事情就不那么简单了。上述场景见于特殊的成员函数,即当有必要时C++自动生成的那些函数。Item17详细讨论了这些函数,但是现在,我们只关心拷贝构造函数和拷贝赋值运算符重载。本节主要致力于讨论C++98中那些被C++11所取代的最佳实践,而且在C++98中,你想要禁止使用的成员函数,几乎总是拷贝构造函数或者赋值运算符,或者两者都是。原创 2024-07-02 15:45:42 · 395 阅读 · 0 评论 -
item10 优先考虑限域`enum`而非未限域`enum`
要写更多的代码,但同时它也避免命名空间污染,防止不经意间使用隐式转换。大多数情况下,你应该会觉得多敲几个(几行)字符作为避免使用未限域枚举这种老得和2400波特率猫同时代技术的代价是值得的。在一些情况下,编译器将会优化速度,舍弃大小,这种情况下它可能不会选择最小的底层类型,但它们当然希望能够针对大小进行优化。那么可能整个系统都得重新编译,即使只有一个子系统——或者只有一个函数——使用了新添加的枚举名。还有第二个吸引人的优点:在它的作用域中,枚举名是强类型。如果我们想让它更一般化,我们还要泛化它的返回类型。原创 2024-07-02 15:19:03 · 345 阅读 · 0 评论 -
item9 优先考虑别名声明而非`typedef`
关于为什么这么实现是有历史原因的,但是我们跳过它(我认为太无聊了),因为标准委员会没有及时认识到别名声明是更好的选择,所以直到C++14它们才提供了使用别名声明的版本。(如果你没有用过模板元编程,太遗憾了,因为如果你真的想成为一个高效C++程序员,你需要至少熟悉C++在这方面的基本知识。在你的项目使用它们之前,你最好看看它们的详细说明书。和别名声明做的都是完全一样的事情,我们有理由想知道会不会出于一些技术上的原因两者有一个更好。是C++98的东西。,也不全是类型转换的工具,也包含一些可预测接口的工具。原创 2024-07-01 11:10:56 · 997 阅读 · 0 评论 -
item8 优先考虑`nullptr`而非`0`和`NULL`
但是重复的调用代码——为互斥量上锁,调用函数,解锁互斥量——更令人遗憾。推导为一个错误的类型(即它们的实际类型,而不是作为空指针的隐含意义),这就导致在当你想要一个空指针时,它们的替代品。可以避开了那些令人奇怪的函数重载决议,这不是它的唯一优势。代码虽然可以这样写,但是就像注释中说的,前两个情况不能通过编译。在C++98中,对指针类型和整型进行重载意味着可能导致奇怪的事情。的值的那个)也可以像我们之前讨论的那样被解析。解释为指针,但是那是最后的退路。本来想表示空指针,但是实际上得到的一个普通的。原创 2024-07-01 09:58:27 · 828 阅读 · 0 评论 -
item7 Moving to Modern C++**
说起知名的特性,C++11/14有一大堆可以吹的东西,auto,智能指针(),移动语义(),lambda,并发()——每个都是如此的重要,这章将覆盖这些内容。掌握这些特性是必要的,要想成为高效率的现代C++程序员需要小步迈进。在从C++98小步迈进到现代C++过程中遇到的每个问题,本章都会一一回答。你什么时候应该用{}而不是()创建对象?为什么别名(alias)声明比typedef好?constexpr和const有什么不同?常量(const)成员函数和线程安全有什么关系?原创 2024-06-30 18:25:05 · 1089 阅读 · 0 评论 -
item6 auto`推导若非己愿,使用显式类型初始化惯用法
当你越熟悉你使用的库的基本设计理念,你的思维就会越活跃,不至于思维僵化认为代理类只能在这些库中使用。很少会出现源代码全都用代理对象,它们通常用于一些函数的返回类型,所以通常能从函数签名中看出它们的存在。出于同样的原因,如果你故意想用整数类型存储一个表达式返回的浮点数类型的结果,你也可以使用这个方法。这样类型的对象的生命期通常不会设计为能活过一条语句,所以创建那样的对象你基本上就走向了违反程序库设计基本假设的道路。不会推导出你想要的类型。)的例子:所谓代理类就是以模仿和增强一些类型的行为为目的而存在的类。原创 2024-06-30 18:03:37 · 260 阅读 · 0 评论 -
item5 优先考虑`auto`而非显式类型声明
auto。原创 2024-06-29 02:15:07 · 340 阅读 · 0 评论 -
item4 学会查看类型推导结果
Item 4: Know how to view deduced types选择使用工具查看类型推导,取决于软件开发过程中你想在哪个阶段显示类型推导信息。我们探究三种方案:在你编辑代码的时候获得类型推导的结果,在编译期间获得结果,在运行时获得结果。在IDE中的代码编辑器通常可以显示程序代码中变量,函数,参数的类型,你只需要简单的把鼠标移到它们的上面,举个例子,有这样的代码中:IDE编辑器可以直接显示推导的结果为,推导的结果为。为此,你的代码必须或多或少的处于可编译状态,因为IDE之所以能提供这些信息是因为原创 2024-06-27 13:31:15 · 594 阅读 · 0 评论 -
条款三:理解`decltype`
在这个模板中,我们不知道我们操纵的容器的类型是什么,那意味着我们同样不知道它使用的索引对象(index objects)的类型,对一个未知类型的对象使用传值通常会造成不必要的拷贝,对程序的性能有极大的影响,还会造成对象切片行为(参见。举个例子,假定我们写一个函数,一个形参为容器,一个形参为索引值,这个函数支持使用方括号的方式(也就是使用“这样的代码将会把你送上未定义行为的特快列车,一辆你绝对不想上第二次的车。“符号指出函数的返回类型,尾置返回类型的好处是我们可以在函数返回类型中使用函数形参相关的信息。原创 2024-06-20 15:20:07 · 893 阅读 · 0 评论 -
item2 理解auto类型推导
推导落入了这里发生的第二种类型推导——模板类型推导的范围。就像注释说的那样,在这种情况下类型推导将会失败,但是对我们来说认识到这里确实发生了两种类型推导是很重要的。类型的变量,这个陷阱也导致了很多C++程序员抛弃花括号初始化,只有不得不使用的时候再做考虑。类型推导和模板类型推导有一个例外使得它们的工作方式不同,接下来我们要讨论的就是那个例外。模板类型推导包括模板,函数,形参,但。的类型说明符——的不同特征,把模板类型推导分成三个部分来讨论。类型推导和模板类型推导几乎一样的工作,它们就像一个硬币的两面。原创 2024-06-19 10:48:24 · 871 阅读 · 0 评论 -
理解模板类型推导
C++98有一套类型推导的规则:用于函数模板的规则。C++11修改了其中的一些规则并增加了两套规则,一套用于auto,一套用于decltype。C++14扩展了auto和decltype可能使用的范围。类型推导的广泛应用,让你从拼写那些或明显或冗杂的类型名的暴行中脱离出来。它让C++程序更具适应性,因为在源代码某处修改类型会通过类型推导自动传播到其它地方。但是类型推导也会让代码更复杂,因为由编译器进行的类型推导并不总是如我们期望的那样进行。原创 2024-06-18 11:08:12 · 391 阅读 · 0 评论