Visual Studio 2019中的C++ 17/20特性和修复

Visual Studio 2019版本16.0 现在可用 是吗 二进制兼容 在VS 2015/2017中,在VS 2019的第一个版本中,我们从C++ 20的工作文件中实现了更多的编译器和库特性,并实现了更多的功能。 <charconv> 重载(C++ 17的“最终boss”),并修复了许多正确性、性能和吞吐量问题。下面是C++ 17/20编译器/库特征工作和库修复的列表。像往常一样,许多编译器错误也被修复了,但是这里没有列出它们;编译器修复往往是特定于某些神秘的代码模式。我们最近在博客上 VS2019中的编译器优化和构建吞吐量改进 ,我们维护了一个关于 VS2019中的编译器一致性改进 .)

null

新功能:

  • 实施 P1164页 从C++ 20无条件地。这就改变了 std::create_directory 检查失败时目标是否已经是目录。以前,所有 ERROR_ALREADY_EXISTS 类型错误已转换为成功,但未创建目录代码。
  • 迭代器调试功能已被教导正确地展开 std::move_iterator . 例如, std::copy(std::move_iterator<std::vector<int>::iterator>, std::move_iterator<std::vector<int>::iterator>, int*) 现在我们可以开始行动了 memcpy 快速通道。
  • 标准库的宏化关键字执行 <xkeycheck.h> 固定的是发出实际问题关键字,而不是通用消息,寻找C++ 20关键字,并避免欺骗智能信息说随机关键字是宏。
  • 我们补充道 长荣2221 operator<<(std::ostream, nullptr_t) 为了写作 nullptr 把它变成溪流。
  • 的并行版本 is_sorted , is_sorted_until , is_partitioned , set_difference , set_intersection , is_heap ,和 is_heap_until 由Miya Natsuhara实施。
  • 第0883页 “修复原子初始化”,它改变了 std::atomic 初始化包含的值 T 在我们的标准库中使用Clang/LLVM时实现了(也是由Miya实现的),而不是默认的初始化。对于c1x,这是当前禁用的,作为constexpr处理中的错误的解决方法。
  • 实现了“太空船”的三向比较运算符 第0515页 “一致比较”,部分支持C++ 20 <compare> 标题如中所述 第0768页 (具体而言,比较类别和 common_comparison_category 类型特征,而不是正在WG21中重新设计的比较算法)(由Cameron DaCamara在编译器中实现。)
  • 实现新的C++ 20 第1008页 聚合规则:具有用户声明的构造函数的类型(即使是默认的或删除的,以便不是用户提供的)不是聚合(由Andrew Marino在编译器中实现。)
  • 实施 remove_cvref remove_cvref_t 类型特征来自 P0550型 ,这是方便剥离参考度和cv资格,但没有衰减函数和数组的指针(这是 std::decay std::decay_t 是的)。
  • C++ 17 <charconv> 浮点 to_chars() 已改进:最短 chars_format::fixed 速度快了60-80%(感谢谷歌的Ulf Adams建议的长除法),并且最短/精确 chars_format::hex 已完成。最短固定记数法的进一步性能改进已经实施,并将在未来的VS2019更新中发布,同时将完成十进制精度重载 <charconv> 实施。
  • C++ 20 第0941页 特性测试宏现在在编译器和STL中完全受支持,包括 __has_cpp_attribute 由Phil Christensen实施。作为提醒,无论选择了标准模式选项,功能测试宏始终处于活动状态(即,已定义或未定义,取决于相关功能的可用性),因为使它们有条件于 /std:c++latest 会在很大程度上挫败他们的目的。

正确性修复:

  • std::allocator<void> , std::allocator::size_type ,和 std::allocator::difference_type 已取消否决。
  • 假的 static_cast 不是偶然压制的标准所要求的 C4244变窄警告 已从中删除 std::string . 正在尝试呼叫 std::string::string(const wchar_t*, const wchar_t*) 现在将正确发射C4244“缩小a wchar_t 变成一个 char .”
  • 固定的 std::filesystem::last_write_time 尝试更改目录的上次写入时间时失败。
  • std::filesystem::directory_entry 的构造函数被更改为存储失败的结果,而不是在提供不存在的目标路径时引发异常。
  • std::filesystem::create_directory 的2参数版本已更改为调用1参数版本,作为基础版本 CreateDirectoryExW 功能将执行 copy_symlink existing_p 是个符号链接。
  • std::filesystem::directory_iterator 当遇到断开的符号链接时不再失败。
  • std::filesystem::space now accepts 相对路径。
  • std::filesystem::path::lexically_relative 不再被后面的斜杠所迷惑,报告为 LWG 3096型 .
  • 四处工作 CreateSymbolicLinkW 拒绝中带有正斜杠的路径 std::filesystem::create_symlink .
  • 解决了Windows10LTSB1609上存在的POSIX删除模式删除功能,但实际上无法删除文件。
  • std::boyer_moore_searcher std::boyer_moore_horspool_searcher 的复制构造函数和复制赋值运算符现在实际上是复制对象。
  • 并行算法库现在正确地使用了实数 WaitOnAddress 在windows8和更高版本上,而不是总是使用windows7和更早的假版本。
  • std::system_category::message() 现在从返回的消息中删除尾随空格。
  • 一些可能导致 std::linear_congruential_engine 触发被0除已被修复。
  • 我们在VS 2017 15.8中首次为程序员用户集成公开了迭代器展开机制(如中所述) https://devblogs.microsoft.com/cppblog/stl-features-and-fixes-in-vs-2017-15-8/ )不再展开从标准库迭代器派生的迭代器。例如,从 std::vector<int>::iterator 并尝试自定义行为现在在调用标准库算法时获取其自定义行为,而不是指针的行为。
  • 无序容器保留函数现在实际上保留了N个元素,如中所述 LWG 2156型 .
  • 许多STL内部容器函数都是私有的,以改善IntelliSense体验。在以后的版本中,还需要额外的修复程序将成员标记为私有 MSVC公司 .
  • 传递到并发库的溢出时间(例如。 condition_variable::wait_for(seconds::max()) )现在已经得到了正确的处理,而不是在一个看似随机的29天周期内(当uint32被底层Win32 API接受的毫秒数溢出时)导致行为改变的溢出。
  • 异常安全正确性问题,其中基于节点的容器 list , map ,和 unordered_map 如果修好的话会变得腐败。在 propagate_on_container_copy_assignment propagate_on_container_move_assignment 在重新分配操作中,我们将用旧分配器释放容器的sentinel节点,在旧分配器上进行POCCA/POCMA分配,然后尝试从新分配器获取sentinel节点。如果此分配失败,则容器已损坏,甚至无法销毁,因为拥有sentinel节点是硬数据结构不变的。修复了这个问题,在销毁现有的sentinel节点之前,从源容器的分配器分配新的sentinel节点。
  • 这些容器被固定为始终复制/移动/交换分配器 propagate_on_container_copy_assignment , propagate_on_container_move_assignment ,和 propagate_on_container_swap ,即使对于声明的分配器也是如此 is_always_equal .
  • std::basic_istream::read 已修复为暂时不写入所提供缓冲区的部分,作为=>处理。这就放弃了我们在VS 2017 15.8中获得的一些性能优势,因为读取的大小大于4k,但是通过避免每个字符3个虚拟调用而提高的效率仍然存在。
  • std::bitset 的构造函数不再按相反顺序读取大位集的1和0。
  • 实施时 P0083号 “拼接地图和集合”,我们设法忽略了 merge extract 除了接受左值容器的重载外,关联容器的成员还应具有接受右值容器的重载。我们已经通过实现rvalue重载纠正了这个疏忽。
  • 随着 只是我的代码步进 特点,我们不再需要为客户提供定制化机械 std::function std::visit 达到同样的效果。移除该机器基本上没有用户可见的效果,只是编译器将不再生成在第15732480行或第16707566行上指示问题的诊断 <type_traits> <variant> .
  • 这个 <ctime> 标题现在正确声明 timespec timespec_get 在命名空间中 std 除了在全局命名空间中声明它们之外。
  • 我们修复了 pair 的赋值运算符 长荣2729 “失踪的斯芬娜” std::pair::operator= “; 它现在正确地接受可转换为配对的类型。
  • 修正了一个小的type traits bug,其中addu const 等应该是一个非推断上下文(即它需要是 typename add_const<T>::type ,不是 const T ).

标题包含:

标准库的物理设计进行了实质性的修改,以避免在不需要时包含头文件。许多客户希望使用标准库容器,但不希望使用iostreams和locale。但是,C++标准库在组件之间具有循环依赖性:

  • 依赖于 <locale> 要使用的面 std::string 作为其底层实现的一部分。
  • std::string 需要流插入运算符,这取决于 std::ostream ,这取决于 <locale> .
  • 历史上,我们的标准库通过引入较低级别的头来解决这个问题 <xstring> ,定义了 std::string ,但其他内容 <string> . <xstring> 都会包括在内 <locale> 组件,以及 <string> ,还原定向依赖关系图。然而,这种方法有许多问题:
  • #include <string> 需要拖入所有iostreams机器来提供流插入操作符,即使大多数翻译单元不使用流插入操作符。
  • 如果只包括某人 <ostream> 他们得到了 std::basic_string std::ostream ,但他们做到了 得到 std::basic_string 的流插入运算符 std::string typedef或字符串文本。客户觉得这非常令人困惑。例如,如果有人试图流式传输 std::basic_string 仅包括之后 <ostream> 编译器会打印出一个非常长的诊断语句 operator<< 找不到,列出26个不相关的重载。此外,尝试使用 std::string_literals , std::to_string ,或其他 <string> 组件,将失败,这是混乱的时候 std::basic_string 在其他方面是可用的。

在VS2019中,我们完全不同地解析循环引用。流插入操作符现在可以找到必要的 ostream 使用参数相关查找的组件,允许我们将其与字符串放在同一位置。这就恢复了(的)适当的分层 std::string 在……下面 <locale> 组件),使使用 <string> 不需要拖拽整个iostreams机器。

如果你有很多 .cpp 包含的文件 string 做一些简单的事情,例如:

#include <stdio.h>
#include <string>

void f(const std::string& s) {
  puts(s.c_str());
}

在VS 2017 15.9中,这个程序在7980XE测试机上编译需要244毫秒(平均运行5次),而在VS 2019 16.0中,它只需要178毫秒(约占时间的73%)。

此外,看似不相关的标题 <vector> 他们被卷入了这场混乱。例如, vector 想扔吗 std::out_of_range ,来源于 std::runtime_error ,它的构造函数 std::string . 我们已经为所有抛出站点提供了离线函数,因此 <stdexcept> 在里面 <vector> 是不必要的,已被删除。在VS 2017 15.9中,以下程序以前编译需要177毫秒,但现在只需要151毫秒(85%的时间):

#include <vector>

void f(std::vector<int>& v) {
  v.push_back(42);
}

这种更改的一个缺点是,可能需要添加一些没有包含正确头的程序 #includes . 如果你说 std::out_of_range 在此之前,您可能需要 #include <stdexcept> . 如果您使用的是流插入操作符,那么现在可能需要 #include <ostream> . 这样,只有翻译单位才真正使用 <stdexcept> <ostream> 组件支付编译它们的吞吐量成本。

性能和吞吐量改进:

  • if constexpr 在标准库中的更多位置应用,以提高吞吐量并减少 copy 家庭,像这样的排列 reverse rotate ,并在并行算法库中。
  • STL现在在内部使用 if constexpr 即使在C++ 14模式下也减少编译时间。
  • 并行算法库的运行时动态链接检测不再使用整个页面来存储函数指针数组,因为将此内存标记为只读被认为不再与安全目的相关。
  • std::thread 的构造函数不再等待线程启动,也不再在底层C库之间插入如此多的函数调用层 _beginthreadex 以及提供的可调用对象。以前 std::thread 将6个函数放在 _beginthreadex 以及提供的可调用对象,该对象已减少到只有3个(其中2个是 std::invoke ). 这也解决了一个模糊的定时错误,其中 std::thread 如果系统时钟在某个时刻发生变化,则的构造函数将挂起 std::thread 正在创建。
  • 修复了中的性能回归 std::hash 我们在实施 std::hash<std::filesystem::path> .
  • 一些地方的标准库用来实现正确的捕捉块现在使用析构函数代替。这将导致更好的调试器交互-通过受影响位置的标准库抛出的异常现在将显示为从其原始抛出站点抛出,而不是我们的重新抛出。不是所有的标准库捕捉块都被消除了;我们希望在MSVC的后续版本中,catch块的数量会减少。
  • 次优编码 std::bitset 由内部的条件抛出引起的 noexcept 通过分解抛出路径来确定函数。
  • 这个 std::list std::unordered_meow 家族成员在更多的地方内部使用非调试迭代器。
  • 几个 std::list 成员被更改为在可能的情况下重用列表节点,而不是取消分配和重新分配它们。例如,给定 list<int> 已经有3号了,打电话给 assign(4, 1729) 现在将覆盖前3个列表节点中的int,并使用值1729分配一个新的列表节点,而不是取消分配所有3个列表节点,然后使用值1729分配4个新的列表节点。
  • 标准图书馆呼叫的所有地点 erase(begin(), end()) 已更改为呼叫 clear() 相反。
  • std::vector 现在在某些情况下可以更有效地初始化和擦除元素。
  • <variant> 已被重构,使其更为优化器友好,从而生成更小更快的代码。最引人注目的是, std::visit 而内线现在成了好朋友。
  • 为了提高可读性,我们已经将clang格式应用到STL的头文件中(还有其他手动更改,例如,将大括号添加到所有控制流。)

报告错误:

请让我们知道您对VS2019的看法。您可以通过IDE的报告问题报告错误,也可以通过web,在 开发者社区的C++选项卡 .

比利·奥尼尔、凯西·卡特和斯蒂芬·T。拉瓦维

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享