我是斯蒂芬T。Lavavej,在过去的六年半里,我一直在与Dinkumware合作,在Visual C++中维护C++标准库的实现。 离我上一篇VCBlog文章已经有一段时间了,因为准备发布我们最新的一批更改让我很忙,但现在我有时间写下我们所做的一切!
如果你错过了通知,你可以 下载2013预览版 马上!
注:在这篇文章中,每当我说“2013”没有资格,我的意思是“2013预览和RTM”。 我将明确提到什么时候预览和RTM之间的事情会出现。
快速总结
Visual C++ 2013中的STL 改进C++ 11的一致性 ,包括初始值设定项列表和可变模板 更快的编译时间 . 我们还实现了即将到来的C++ 14标准的功能,包括 使您独一无二 以及 透明算子函子 .
编译器功能
作为提醒,这里是C++ 11核心语言特性,这些特性已经添加到编译器中,在VisualC++ 2013预览中(除了 Visual C++ 2012的特点 ,当然):
*函数模板的默认模板参数
*委派施工人员
*显式转换运算符
*初始化器列表和统一初始化
*原始字符串文本
*可变模板
它们在“Visual C++编译器2012年11月社区技术预览”中可用(它们覆盖在我的 视频演练 )但是编译器团队从那时起已经取得了很大的进展。 也就是说,成吨成吨的bug已经被修复了——其中许多都是由Nov CTP的用户报告的(谢谢!)。
就像赫伯·萨特刚刚在 “C++的未来” 在构建会议上(与同伴) 博客文章 更多的C++ 11核心语言特性将在2013 RTM中实现:
*别名模板
*默认函数(rvalue引用v3除外)
*删除的函数
*非静态数据成员初始值设定项(NSDMI)
关于这个警告:“RValk V3引用”是指如何自动生成移动构造函数和移动赋值操作符的C++ 11规则,以及在声明移动时抑制自动生成的副本的规则。 从2013年预览版到RTM版之间没有足够的时间让编译器团队实现RTM级的质量。 因此,不支持使用=default请求memberwise move构造函数和move赋值运算符。 编译器团队敏锐地意识到这些东西的重要性,包括对STL的重要性,这是他们2013年后RTM的最高优先事项之一。)
此外,一些C99核心语言功能将在2013 RTM中实现:
*C99布尔
*C99复合文字
*C99指定初始值设定项
*C99变量声明
请看赫伯的公告 视频 / 邮递 更多信息,特别是2013年后RTM C++ 11/14一致性路线图 ,包括(包括许多其他东西)RValk引用V3、CONTXEPR和C++ 14通用lambDas。
STL特征
Visual C++ 2013预览中的STL已经完全转换为使用以下C++ 11特征:
*显式转换运算符
*初始值设定项列表
*作用域枚举
*可变模板
在2013年RTM中,该列表将扩展到:
*别名模板
*删除的函数
我说“转换过”是因为STL已经伪造(我想)模拟“将是一个更庄严的词”,这些功能只有库的解决办法,有不同程度的成功,自从Visual C++ 2008 SP1。 更详细地说:
*在核心语言中,显式转换运算符是一个通用功能–例如,可以使用显式运算符MyClass()。 但是,标准库目前只使用一种形式:显式运算符bool(),这使得类可以安全地进行布尔测试。 (普通的“operator bool()”是出了名的危险。) 以前,我们用操作符指针到-member()来模拟显式操作符bool(),这导致了各种各样的麻烦和轻微的低效。 现在,这个“假布尔”的变通方法已经被完全删除。
*我们没有试图模拟初始化列表,尽管我意外地允许一个破碎的/非功能的“初始化”列表列表标题在Visual C++ 2010中被传送,混淆了注意到它存在的用户。 它在Visual C++ 2012中被删除,以避免混淆。 现在编译器支持initializer list,
*作用域枚举是在Visual C++ 2012编译器中实现的,但是由于涉及编译器错误的CL/CLR的长故事,我们不能在STL中使用它。 在VisualC++ 2013中,我们能够去掉我们的伪范围内枚举(通过在名称空间中封装传统的无范围枚举来模拟,这是明显不完美的)。
*多年来,我们用两种不同的“人造可变”预处理器宏系统模拟可变模板–第一个系统反复包含子标题,而第二个系统(更优雅,至于爬行的恐怖去)消除了副标题,并取代他们的大反斜杠继续宏,由其他宏压印出来。 本应是真正可变的函数,如make u shared
在Visual C++ 2013中,我们彻底地消除了伪变量宏。 如果你想要50种类型的元组,现在就可以了。 此外,我很高兴地宣布,切换STL到真正的可变模板已经完成 缩短了编译时间 和 减少编译器内存消耗 . 在STL的开发过程中通常会发生的事情是,标准告诉我们实现更多的东西,所以我们这样做了,更多的东西需要更多的时间和更多的内存来编译(这可以通过正确使用预编译头来减轻)。 然后,编译器的测试人员注意到性能下降并抱怨,我以一副铁石心肠和问心无愧的态度回应,因为标准告诉我要这样做。 但在这种情况下,人造变量是如此令人难以置信的膨胀,删除他们造成了很大的差异。 (解析一个可变模板并不是免费的,但它比解析6或其他重载的模板便宜得多。)
随着编译器、STL及其依赖项(尤其是CRT和并发运行时)的修改,精确的数字会发生变化,但是当我签入最终的变量重写时,我运行了一些测试,可以共享具体的数字。 我的测试包括x86版本模式下的所有STL头。 我实际上没有使用任何东西,但是编译器仍然需要做大量的工作来解析所有的东西,并实例化STL本身使用的东西,因此这是一种合理的方法来测量在不广泛使用STL的情况下拖动STL的开销。 将Visual C++ 2012(默认为“x变量ValdIX最大值=5”)与Visual C++ 2013进行比较,预处理后的文件大小从3.90 MB减小到3.13 MB。 编译时间从2307ms减少到2029ms。 (我只测量了编译器前端;后端在没有优化的情况下花费了150-250毫秒,这里对此不感兴趣。 此外,出于复杂的原因,我将不在这里讨论,我排除了生成预处理翻译单元所需的相对较短的时间——但正如您所见,计算这些时间只会进一步增加差异。) 最后,我通过生成一个预编译头(PCH是编译器内存快照)来测量编译器内存消耗,该头也从40.2 MB减少到33.3 MB。 因此,STL更小,根据每个度量编译更快。 如果你必须增加你的变量ViaRixMax来让你的程序用Visual C++ 2012来编译,那么改进会更加戏剧化(对于完整性,我测量了VixAddiCixMax=10,分别为6.07 MB、3325 ms和58.5 MB)。
(注意:我在2013年3月底执行了这些度量之后,编译器团队已经对一些内部数据结构进行了重大修改。 因此,Preview中的编译器内存消耗,尤其是RTM,可能与我最初的度量有很大的不同。 然而,这并不影响我的结论:孤立地考虑,使用真正的可变模板可以改进每个度量。)
*该标准要求STL中的一些内容是alias模板。 例如,ratio_add
*该标准在整个STL中声明了100多个已删除的函数,通常是为了使类不可复制。 以前,我们用传统的C++ 98/03解决方案模拟:私有未实现声明。 在2013年的RTM中,我们将消除这种解决方法,将自由函数标记为=delete,并按照标准的要求使用=delete公开成员函数(除了类型u info,因为这个边距太窄而无法包含的可怕原因)。
您可能已经注意到,编译器列表中有一些特性不在库列表中。 原因很简单:并非所有的核心语言特性都对标准库有明显的影响。 例如,原始字符串文本不需要相应的标准库实现更改(尽管它们使
您可能还注意到我在标题中提到了C++ 14。 那是因为我们已经实现了下面的标准库特征,这些库已经被投票到C++ 14工作文件中:
*“透明算子函子”少<>、大<>、加<>、乘<>,等等。
*使u唯一
*cbegin()/cend()、rbegin()/rend()和crbegin()/crend()非成员函数
*别名模板使无符号、衰减等(在RTM中实现,但不是预览)
( 请注意 在这个帖子的底部有一个常见问题,“Q1:为什么你在没有完成C++ 11核心语言的时候,你就要实现C++ 14标准库的特性?”
更多信息,请阅读我的建议 编号3421 “使算符函子更大<>”(Standardese投票通过), 编号3588 “使你独一无二”(背景), 编号3656 “使大学独一无二(第1版)”(Standardese投票通过),以及我提出的关于图书馆第2128期的决议“缺少全球功能cbegin/cend”,该决议与其他委员会一起被接受 编号3673 “C++库工作组准备发布Bristol 2013”。 (一个保证哈希
沃尔特E。Brown(与微软没有关联)在年提出了alias模板 N3546型 “转换特性ReDux”,这无疑是最佳的投票到C++ 14。 凡敢于质疑这一论断的人,将被判处五年刻苦劳动,以打字代替打字。
另外,James McNellis(现在维护我们的CRT)使用透明操作符函子将和
修复
这就是它的特点。 现在,为了修复。 这一次,我跟踪了Visual C++ 2012和2013之间的所有STL修复。 这包括通过 微软连接 ,以及内部报告的错误(无论是我发现错误时自己提交的,还是我们的测试人员提交的,还是其他Microsoft团队提交的)。 我的清单应该是详尽无遗的,除了几个例外:我不计算在我们的测试中提交的bug,当它们不涉及损坏的库代码时,我试着省略引入的(令人高兴的是,很少)bug,然后在2012年发布后修复,我不计算那些在库中报告但在编译器中实际解决的bug(例如,一些类型特征bug实际上是我们所依赖的编译器钩子中的bug)。 此外,这里只列出了正式归档的bug。 有时我们会注意到并修复数据库中从未存档的错误(例如,我注意到引用的行为不正确,并修复了该错误,但从未有人报告过)。 我除了提供连接错误编号外,还提供了我们的内部错误编号(例如DevDiv#1729),因此如果有人问我更多详细信息,我可以轻松查找错误。
有几次大修:
*
*
*在C++ 98/03中,生命是容易的。 STL容器要求它们的元素是可复制的,可复制的,无条件地复制(C++ 03 23.1 [ LIB容器。要求] / 3)。 在C++ 11中,只引入了像UNQuYY-PTR这样的可移动类型和引入EMPTITYOBACK()的附加成员函数,容器的要求被改写为细粒度。 例如,如果您有一个列表
*2013 RTM将包含一个
然后是个别的错误修复:
*std::find()的实现有一个优化,可以在可能的情况下调用memchr(),但是find()同时调用memchr()时过于激进,导致了不正确的结果(DevDiv#316853)/ 接#703165 ),而称之为攻击性不够会导致次优性能(DevDiv#468500)/ 接757385 ). 我们重写了这个优化,使之在所有情况下都是正确的和最优的。
*缺少用于比较shared_ptr/unique_ptr和nullptr的运算符(DevDiv#328276)。 有些比较是由于临时表的构造而编译的,但是标准要求操作符的存在,如果你仔细观察,这是可以观察到的。 因此,我们添加了运算符。
*共享的未来可通过标准禁止的方式转换为未来(DevDiv#361611)。 我们阻止了这种转换。
*在
*获取numericŠlimits的静态常量数据成员的地址与/Za编译器选项(DevDivŠ376524)不兼容。 现在是了。
*
*按照标准的要求,tuple_element>现在强制I
*
*
*
*在以前的版本中,iostreams被更改为与/vd2编译器选项一起正常工作,但是这种机制有时会发出编译器警告。 编译器获得了一个新的pragma,#pragma vtordisp,iostreams现在使用它,避免了这种虚假警告(DevDiv#430814)。
*由于输入错误,_VARIADIC_MAX=7未编译(DevDiv#433643)/ 连接#746478 ). 我们修正了这个错误,后来彻底根除了机器。
*系统错误::what()的返回值不符合标准(DevDiv#453372)/ 连接#752770 ). 现在是了。
*codecvt one如果没有各种using声明(DevDiv#453373),就无法编译/ 连接#752773 ). 它不再需要这样的解决办法。
*由于括号放错了位置,
*我们的测试在/clr下发现了一个非常模糊的死锁,当持有locale锁并抛出异常时(例如,当std::locale是从一个伪名称构造的)。 我们重新整理了代码来解决这个问题(DevDiv#453528)。
*在ARM上,我们意识到我们正在以一种多线程不安全的方式减少sharedŧptr/weakŧptr的refcounts(DevDiv 355917)。 (注意:x86/x64完全不受影响。) 尽管我们从未观察到崩溃或其他不正确的行为,即使在集中测试之后,我们也改变了减量以使用顺序一致性,这绝对是正确的(尽管可能比最佳的稍微慢一点)。
*该标准并不保证tupleu size可以用于tuple(或pairs或array)以外的事物。 尤其是tupleŠu size
*C++ 11表示,清单::Erasee()不应该使端迭代器失效(DeViDiOx 461528)。 这是std::list表示的物理结果,所以发布模式总是正确的,但是我们的调试检查抱怨无效。 我们修复了它们,因此它们现在认为要保留结束迭代器。
*使用标志regex::icase | regex::collate构造正则表达式会导致区分大小写的匹配,这与标准(DevDiv#462743)相反。 现在它的结果是不区分大小写的匹配。
*构造一个std::线程导致ŠCrtDumpMemoryLeaks()(DevDivŠ467504)报告的内存泄漏/ 接757212 ). 这并不是一个严重的、无限制的泄漏——所发生的是,内部数据结构的一次性初始化正在分配标记为“正常块”的内存(由泄漏跟踪机器观察),而且在CRT关闭时也没有被注册进行清理。 我们通过将此分配标记为“CRT块”(默认情况下不包括在报告中)并将其注册以进行清理来修复此问题。
*对从打包的任务中获得的未来调用wait_for()/wait_until()将返回future_status::deferred immediate,这是不应该发生的(DevDiv#482796)/ 接#761829 ). 现在,它根据标准的要求返回futureu status::timeout或futureu status::ready。
*C++ 11的最小化分配器接口不需要分配器提供嵌套的ReNe结结构,但VC没有正确实现这个(DeViDi)483844。 接#762094 ). 我们已经按照标准安装了这个模板机器, N3690型 17.6.3.5[allocator.requirements]/3:“如果allocator是SomeAllocator
*另一个最小化的分配器接口错误-在调试模式下,std::vector直接使用分配器,而不是通过分配器特性,这导致最小分配器不工作(DevDiv#483851)/ 接#762103 ). 我们修复了这个问题,并审核了所有容器以排除类似问题。
*为std::condition_变量供电的并发运行时中的一个bug导致崩溃(DevDiv#485243)/ 接#762560 ). ConcRT解决了这个问题。
*人类永恒的敌人vector在x86上崩溃,指数超过20亿(DevDiv#488351)/ 接#763795 ). 请注意,20亿压缩位仅占用256MB,因此这是完全可能的。 我们修正了我们的数学,这样就行了。
*指针没有使用用户定义的空指针(DevDiv#491103)编译/ 接#764717 ). 我们重新设计了实现,这样就可以编译(而不是试图形成void&这是禁止的)。
*尽管根据标准它是正确的,merge()的实现执行的迭代器比较比需要的要多(DevDiv#492840)。 我们将此(及其相关实现)更改为最佳。
*timeŠput的返回值对于char是正确的,但是对于wcharŠt是不正确的,因为实现无意中发生了分歧(DevDivŠ494593)/ 接#766065 ). 现在wchar像char一样工作。
*C++ 11表示ISTrAMBufyItAuth::操作符*()应该按值返回图(DeViDi 494813)。 现在我们这样做。
*从setlocale()返回的const char*构造std::locale可能会崩溃,因为std::locale的构造函数在内部调用setlocale()! (德夫迪夫#496153/ 接#766648 ) 我们通过总是将构造函数的参数存储在std::string中来解决这个问题,它不能像这样无效。
*
*pow(-1.0,complex
*从nullptr构造共享的Šptr是分配一个引用计数控制块,这是标准(DevDivŠ520681)所禁止的/ 连接#771549 ). 现在这样的共享资源真的是空的。
*该标准对unique的操作顺序(DevDiv#523246)非常严格/ 连接#771887 ). N3690型 20.9.1.2.5[unique.ptr.single.modifiers]/4:“void reset(pointer p=pointer())noexcept;效果:将p赋给存储指针,然后如果存储指针的旧值oldu p不等于nullptr,则调用getu deleter()(oldu p)。[注意:这些操作的顺序很重要,因为对getu deleter()的调用可能会破坏*this-“尾注]” 我们现在完全遵循标准。
*有趣的是,标准允许iostream绑定到它们自己,但这在我们的实现中触发了堆栈溢出(DevDiv#524705)/ 接#772293 ). 我们现在可以检测自我捆绑并避免碰撞。
*标准要求minmax_element()来查找 最后的 最大元素(DevDiv#532622),与查找第一个最大元素的maxŠu element()不同。 这个选择不是武断的——它是minmaxu element()实现的基本结果。 我们修复了minmax_element()以遵循标准,并仔细审核了它的正确性。
*我们的实现将put#u time()声明为taking tm*(DevDiv#547347/ 连接#773846 ). 现在它需要const tm*作为标准的要求。
*在2012年11月的CTP中,在我们的标准库更改准备就绪之前发布,编译器团队提供了一个“假”
*system_clock::to_time_t()尝试执行舍入,但在给定大量输入时触发整数溢出(DevDiv#555154)/ 接#775105 ). 在标准允许的情况下,我们现在执行截断,使我们不受整数溢出的影响。
*该标准规定前向迭代器标记不应派生自输出迭代器标记(DevDiv#557214)/ 连接#775231 ),但在我们的实现中确实如此。 我们已经停止这样做了,我们已经改变了代码的其余部分来补偿。
*由于与运算符fake-bool()交互的一个模糊编译器错误,带有lambda deleters的唯一ptr并不总是布尔可测试的(DevDiv#568465)/ 连接#775810 ). 现在unique_ptr有了显式操作符bool(),这个bug已经被彻底根除了。
*在Visual C++ 2010和2012之间,我们引入了一个回归,其中解析带有IoFikes的浮点数(例如“ISS> dBL”)会得到最后一个错误,而CRT的STROTD()不受影响。 我们修复了iostreams以获得所有正确的位(DevDiv#576315)/ 连接#776287 ,也称为DevDiv#616647/ 连接#778982 –在RTM中修复,但不是预览)。
*
*
*inplaceu merge()是标准中为数不多的非常特殊的算法之一–它分配额外的内存以完成其工作,但是如果它不能分配额外的内存,它会退回到较慢的算法,而不是失败。 不幸的是,我们实现的回退算法是不正确的(DevDiv#579795),这在很长一段时间内没有引起注意,因为在21世纪没有人会耗尽内存。 我们修复了inplaceu merge()的回退算法,并审核了所有回退算法的正确性(包括稳定性)。 我们还添加了回归测试(能够根据需要将Vulcan神经夹送到operator new()/等),以确保这种情况不会再次发生。
*由于一个off by one错误,future_errc的message()和what()不起作用(DevDiv#586551)。 这是在标准开始说未来的u errc不应该从0开始时引入的,我们相应地更改了实现—但没有注意到消息翻译仍然假设基于0的索引。 我们已经修好了。
*basic的regex
*标准要求由空函数指针、空成员指针和空std::函数构造的std::函数为空。 我们的实现是构造存储空函数指针等的非空std::函数,调用时会崩溃。 我们按照标准(DevDiv#617384)对此进行了修复/ 连接#779047 –在RTM中修复,但不是预览)。
*由于模板元编程的微妙性(DevDiv#643180),指针u traits
*在某些情况下,我们的迭代器调试机制是先锁定,然后不执行操作,这是毫无意义的慢(DevDiv#650892)。 这发生在两种情况下:_ITERATOR_DEBUG_LEVEL=1(这从来不是默认值)和deque。 我们已经修复了这个问题,所以只有在有实际工作需要保护时,才会在_ITERATOR_DEBUG_LEVEL=2中使用锁。
*C++ 11表示,列表::SPLICE()不使迭代器无效,它只传送受影响的迭代器(DeViDiα671816)。 连接#785388 ). 这是一个物理保证,但是我们的调试检查认为这样的迭代器是无效的。 我们已经更新了检查,所以他们严格遵循C++ 11的规则。 (注意:forwardu list::spliceu after()仍受影响;我们计划在将来解决这个问题,但不是在2013年
*
*Lambdas捕获引用包装器在某些情况下不会编译(DevDiv#704369/ 接#788701 ). 既然我们已经修复了referenceu wrapper中的一致性问题,那么这段代码就可以干净地编译了。
*按下空格键(xkcd#1172)时,cin不再使CPU过热。
突破性变化
在这一点上,这些特性和修复源于源代码更改——即使在使用Visual C++ 2012编译的情况下,您也必须更改代码以符合C++ 11。 下面是一个非详尽的列表,所有这些都已在实际代码中观察到:
*调用std::min()或std::max()时,必须#包括
*如果您的代码确认了我们的假作用域枚举(包装在名称空间中的传统非作用域枚举),则必须对其进行更改。 例如,如果您指的是类型std::futureu status::futureu status,那么现在您必须说std::futureu status。 请注意,大多数代码不受影响—例如,std::futureu status::ready仍然可以编译。
*类似地,如果您的代码确认了我们的假别名模板,则必须将其更改为2013 RTM。 例如,您必须说allocatorU traits::rebindU alloc::other而不是allocatorU traits::rebindU alloc。 有趣的是,尽管ratio_add
*显式运算符bool()比运算符fake-bool()严格。 显式运算符bool()允许显式转换为bool(例如,给定共享的ptr
*现在我们使用的是真正的可变模板,我们没有定义可变的最大值及其未指定的共谋者。 如果您仍在定义变量最大值,我们不会抱怨,但不会有任何影响。 如果你承认我们的人造可变模板宏机器在任何其他方式,你必须改变你的代码。
*除了普通的关键字,STL头现在严格禁止对上下文敏感的关键字“override”和“final”进行宏化。
*referenceu wrapper/ref()/cref()现在严格禁止绑定到临时对象。
*
*各种STL类型特征都有一个前提条件:T应是一个完整的类型。 现在编译器更严格地执行了这一点,尽管我们不能保证它在所有情况下都是强制执行的。 (STL前提条件冲突会触发未定义的行为,因此该标准不保证强制执行。)
*STL不尝试支持/clr:oldSyntax.
常见问题
Q1:为什么你还没有完成C++ C++ 14标准库的功能,而你还没有完成C++ 11核心语言呢?
A1:这个问题问得好,答案很简单。 我们的编译器团队非常清楚C++ 11核心语言的特点,这些功能有待于实现。 我们在这里实现的是C++ 14标准库的特性。 编译器开发人员和库开发人员是不可互换的——如果我的生命依赖于它,我就无法实现主要的编译器功能(即使是静态的断言也要花费我几个月的时间才能弄清楚),我喜欢认为情况正好相反,尽管火箭科学家可能比火箭科学的比萨饼送货员更擅长比萨饼送货。
问题2:很公平,但您之前提到了“C++14通用lambdas”。 为什么您的编译器团队计划实现 任何 C++ 14内核语言特点在整理前 全部的 C++ 11核心语言的特点?
A2:正如赫伯喜欢说的那样,“C++ 14完成C++ 11”。 编译器团队正在追求完全C++ 14的一致性,并将所有C++ 11和C++ 14的特性看作是统一的工作项桶。 他们正在根据客户需求(包括库需求)和实现成本对这些功能进行优先级排序,以便尽快交付最有价值的功能。 功能的优先级不受其在工作文件中的投票时间的影响。 因此,他们的后3-RTM一致性路线图在价值较低的C++ 11特征(例如属性)之前放置了非常有价值的C++ 14特征(例如通用lambda)。 再说一遍,请看赫伯的公告 视频 / 邮递 更多信息。
Q3:C99标准库通过引用合并到C++ 11中的情况如何?
A3:好消息——我的同事Pat Brenner和Dinkumware一起胡选了大量的C99,并将它们集成到Visual C++ 2013的CRT中。 我们还没有完成,但我们正在取得进展。 不幸的是,我没有时间处理STL中相应的包装头(
Q4:这些编译器/库特征将在Visual C++ 2012更新中进行,还是我们必须等待Visual C++ 2013?
A4:你必须等待Visual C++ 2013。 我知道这不是大多数人想听到的,所以请允许我解释一下。
我是个程序员,如果你在读这篇文章,我想你也是个程序员。 所以,作为程序员,让我们一起来看看下面的区别。 这是UTUPLE >从Visual C++ 2012到2013的变化,正如我们的内部差异工具“奇”所看到的:
请特别注意左侧的视觉摘要。 从技术上讲,这个差异是 可怕的怪物 .
VS-Update机制主要用于发布高优先级的错误修复,而不是发布新特性,特别是带有中断性更改的大规模重写(与同样大规模的编译器更改相关)。
像Visual C++ 2013这样的主要版本给了我们改变和打破很多东西的自由。 我们根本不可能在更新中发布这些东西。
问题5:那些错误修复程序呢? 我们能更新一下吗?
A5:这是一个有趣的问题,因为答案取决于我的选择(而在上一个问题中,即使我愿意,也不允许我在更新中进行这样的重写)。
每个团队都可以选择将哪些错误修复带到“shiproom”,以便考虑将其包含在更新中。 有些事情shiproom不会让我们逃脱(例如,在主要版本之外禁止二进制破坏性的更改),但否则我们就有了决定事情的自由。 我个人优先考虑带宽而不是延迟——也就是说,我更喜欢在每个主要版本中发布更多的错误修复,而不是在多个更新中更频繁地发布更少的错误修复(在同一时间段内)。
更详细地说,backporting修复需要非零的时间(特别是当分支由于累积的更改而出现分歧时)。 这也是更危险的——正如你所听到的,C++是一个复杂的世界,显然简单的改变会产生意想不到的后果。 即使它只是发出一个虚假的警告,我们也不希望更新破坏东西。 在主要版本中修复东西给了我们时间来修复补丁,让一切都完全正确。
我们经常备份STL修复(例如在Visual C++ 2010 RTM中,有一个STD::由小字符串优化导致的String内存泄漏