在VS2015 Update 3 中关于STL的修复

[原文发表地址] 在VS2015 Update 3中关于STL的修改

[原文发表时间] 2016/8/12

大概在一个月以前, 我们发布了VS 2015 Update 3 。 下面详细地列举了其中关于STL 的修复。

(之前的更新日志:RTM Part 1, RTM Part 2, Update 1, Update 2 features, 和Update 2 fixes.)

 

STL 模块

/std:c++latest 引入了下面的新特性: P0025R1 “clamp()”, P0185R1 “is_swappable, is_nothrow_swappable”以及 P0272R1 “Non-const basic_string::data()”.

 

然而/std:c++latest 也摒弃了一些旧特性:N4190 “移除了auto_ptr, random_shuffle()以及旧的<functional> Stuff “,P0004R1 “移除了不合理的Iostreams 别名”, LWG 2385 “function::assign allocator 参数没有意义”以及各种各样的非标准特性( std::tr1 命名空间, 一些TR1-only machinery,以及std::identity 结构体)。

编译选项/std:c++14 和 /std:c++latest 是从Update 3才开始新加进来的。

/std:c++14(默认情况下)定义了宏:_MSVC_LANG 为201402 并且在这种情况下,支持所有C++14模块也支持该特性,并且使移植到Update 2 上的C++ 17 新特性集合生效(这个是完全不同于Clang 和GCC 中的 –std = c++14 模式)

/std:c++latest 定义了_MSVC_LANG > 201402 (这个精确值是随时改变的)并且声明:在这种情况下所有的甚至包含已经移除的模块均支持该特性 ; 于我自己而言,很乐意去确认最新的工作文件以及任何源文件的重大改变。

因为我们STL的实现方案是支持three-and-a-half 编译器(C1XX, Clang, EDG-imitating-C1XX, 以及EDG-imitating-Clang), 我们专门有一个独立的宏用来控制STL 的模式。 如果它被定义了, 我们监控_MSVC_LANG即可, 否则监控__cplusplus 。(在所有的C++11的特性实现之前,C1XX 将不会改变_MSVC_LANG 的值)

 

更进一步当STL 定义_HAS_CXX17 等于0或者1, 这个通常用来控制我们头文件的一些行为。 我们支持重载这个宏,但也仅仅在特定的环境下。/std:c++latest /D_HAS_CXX17=0 支持新的编译器但却是旧的STL 的实现。然而,/std:c++14 /D_HAS_CXX17=1却恰恰相反, 即旧的编译器,新的STL实现。 我们并没有故意来圈定这个, 再说了这也只是暂时的, 而且它很可能在将来的某个时间停止工作或者失效。(无论在任何情况下新的STL 特性都应该是支持一个新的编译器模块)。

 

一般而言, LWG 问题一定会被解决的。这个将不会受到HAS_CXX17的控制。

 

最后, 尽管我们还没有对C+=17 STL 特性的完全支持, 但是我们为需要移除的模块定义了独立的宏, 他们是_HAS_AUTO_PTR_ETC, _HAS_OLD_IOSTREAMS_MEMBERS, _HAS_FUNCTION_ASSIGN, _HAS_TR1_NAMESPACE, 以及 _HAS_IDENTITY_STRUCT。

它们定义为0还是1这都依赖于_HAS_CXX17的值。 但是他们是可以被重载的(并且我们支持它们之间的整合)。

 

LWG 问题

我们实现了C++ 14 的LWG 2064 “在basic_string 中关于noexcept 的问题“

 

我们也实现了C++17 的LWG 2296 “std::addressof 应该是 constexpr”,LWG 2596 “vector::data() 应该用 addressof”, 以及LWG 2688 “clamp 丢失了一些前提条件 , 就结果而言多了一些冗余条件“。

 

STL 修复

 

在Update2, 在internal _Destroy_range()的协助下 我们移除了一些metaprogramming。在发布版本中这种程序是不需要的, 但是移除它之后可以大大地改善debug 模式的性能(VSO#213185, VSO#218398/Connect#2661660, VSO#219797/Connect#2683478)。 在Update 3之后。 我们通过恢复和改善metaprogramming修复了很多regression 问题。 跟Pre-Update2 metaprogramming不同, 就当下而言这个新的metaprogramming调用了用户自定义allocator的destroy()。为了恢复pre-Update 2 的调试性能, 用户自定义的分配器常常需要用C++11 最小的分配器接口, 这通常都需要通过移除construct() 和destroy() 成员函数。

 

通过调用和用户自定义分配器当然不仅是std::allocator (VSO#189231)来优化metaprogramming从而改善性能。当用户定义的分配器不再提供construct() 和 destroy()函数时这种优化也是可以被实现的。

在x86上关于atomic<int64_t>/atomic<uint64_t>修复了一些关于冗余代码产生

除在必要情况下之外, STL 现在可以避免使用thread-safe “magic statics” 从而改善代码产生

Tuple 的forwarding constructor 现在可以避免拷贝构造产生冲突, 在某些特定的场景下修复编译错误(VSO#215996/Connect#2632801, VSO#216014/Connect#2633029)。

通过巧妙地转移到memcmp 和memchr, std::equal 和 std::lexicographical_compare的性能会被大大地改善, 这也将会促进在一些场景下性能的改进; 例如: 在64位平台上equal(char*,char*,char*, char*) (VSO#180464)。

在我们的迭代器调试机制中减少bookkeeping 函数的调用次数,可以大大改进调试build 的性能。

随着调试build 大量的改进, 在发布build 中另一个40% 的VS update 2 中关于string::push_back 性能的改善。

通过除去几种支持调用的空模板metaprogramming(比如:_Iter_cat)在所有的标准算法中改善debug build 的性能。

通过减少函数对象拷贝的数量, 改进调用函数对象的所有算法。(VSO#91245).

应用改进的_SCL_INSECURE_DEPRECATE 信息确保维持原有的算法(在Update2 中只有一小部分的算法被纳入到淘汰行列)。当调试标准算法的时候,这也将改进调试用户体验,然而将不再有任何的_ITERATOR_DEBUG_LEVEL 的预处理程序相互声明标准算法的多种拷贝(VSO#180466)。

在C++14 的dual-range 算法和我们有点不太标准的_ITERATOR_DEBUG_ARRAY_OVERLOADS之间, 因为相互作用而不能正常地编译像mismatch(iter, iter, T[]) 这类的函数, 目前我们已经修复了这类问题(VSO#211972)。

通过排列检查很可能在迭代之前的第二个排列来改进在_ITERATOR_DEBUG_LEVEL 1 和 2的情况下std::mismatch 的性能。

修复在STL 中出现的警告C4242 ,这个警告是默认关闭的(VSO#194854)。请注意我们是逐个修复了这些非常罕见的默认应该关闭的警告。 原则是要维持/W4 /analyze 的的精准性,然而我们并没有尝试去确保/Wall 的精准性。

修复关于std::pop_heap 的整型溢出问题(VSO#204655)。

在/clr:pure 因为调用CLR-inserted LoadLibrary 引起了一些潜在的挂起。 目前已经修复了此类问题。

修复atomic<UDT>::operator=()的返回值(VSO#197633/Connect#2430244). 请注意:atomic<integral> 和atomic<T *> 之间是不会相互影响。

通过make_shared() 来改进exception_ptr 的性能。

减少正则匹配时栈空间的损耗,这在很大程度上将会避免栈溢出。(VSO#208146)。

用result_type 代替unsigned long 来修复subtract_with_carry_engine::seed()的问题。(VSO#214595/Connect#2610325)。

当m == numeric_limits<result_type>::digits 时, 修复了subtract_with_carry_engine的问题。

我们规避了更多的关于在STL 头文件里面使用非标准编译器拓展,确保统一性并使Clang 更加有用(VSO#192363)。 注意:iostreams 头文件包含了_Getvals()的三种类的实例化, 这也是一个不太规范的实现, 因为考虑到二进制兼容性问题,所以在Update 3 中暂时维持不变,但是将会在STL 的下一个主要版本中进行修复。(因为STL 的头文件是系统头文件,在不出现任何警告的境况下Clang 接受这些头文件。)

从RTM 乃至到Update2, STL 包含了一些不成文的“安全舱口”这用来限定哪些常量表达式将会被使用,这引入了将在所有STL 的头文件里面出现_CONST_FUN and _CONST_DATA。事实证明这也将是多余的,因此在Update3 中,我们放弃了这些宏。现在STL 的头文件可以无条件地使用“constexpr”。

在Clang 中compiler hook 可以强化std::is_assignable(这之前是C1XX 来实现的), 这也将简化STL 头文件并且潜在地提高了编译器的吞吐量。

在Clang 和C1XX 中compiler hook可以强化std::addressof(), 允许它作为constexpr, 并且提高了编译器的吞吐量。

STL 容器和迭代器现在支持“fancy pointers”。

在locale 代码中的函数模板可以防止逗号运算符的重载。

STL 现在可以正确地处理POCS 分配器(propagate_on_container_swap)。注意:在引入silent bad codegen 之前,都是在错误地处理POCCA/POCMA (propagate_on_container_copy_assignment/propagate_on_container_move_assignment)。 这也将会在STL的下一个发布版本中被修复。

 

Billy Robert O’Neal III – @MalwareMinigun

SDE II – Visual C++ Libraries

bion@microsoft.com

Casey Carter – @CoderCasey

SDE II – Visual C++ Libraries

cacarter@microsoft.com

 

Stephan T. Lavavej – @StephanTLavavej

Senior Developer – Visual C++ Libraries

stl@microsoft.com