STL Fixes In VS 2015, Part 2
In addition to implementing a whole bunch of features in VS 2015’s C++ Standard Library, we’ve also fixed a whole bunch of bugs. A year ago, I listed the STL fixes in VS 2015 CTP1. Now, it’s time to list the STL fixes between CTP1 and RTM.
We originally shipped bind()/function/mem_fn()/reference_wrapper/etc. as part of TR1 in VS 2008 SP1. As TR1 evolved into C++0x and then into C++11, we updated our implementation to use rvalue references, decltype, and variadic templates. While this mostly worked, the TR1-era machinery was extremely complicated, and it accumulated a large number of bugs over the years. We don’t want the Standard Library to “mostly work” – we want crystalline perfection. So, I rewrote <functional> almost entirely from scratch, keeping only C++03’s stuff and std::function’s skeleton. This significantly reduced the size of the STL’s source code (by 24 KB, 935 editor lines). Except for one limitation (Expression SFINAE in function/result_of), overhauling <functional> fixed all known bugs:
* In certain scenarios, bind() wouldn’t compile with a function object inheriting operator()() from a base class (DevDiv#617421/Connect#779061).
* bind() wasn’t perfectly forwarding unbound arguments passed through placeholders like _1 (DevDiv#343411/Connect#717188, DevDiv#410033/Connect#737872, DevDiv#862588/Connect#814028).
* bind()’s function call operator wasn’t const-overloaded (DevDiv#535246/Connect#773565, DevDiv#1034816/Connect#981289). My original fix accidentally tried to use Expression SFINAE, but I was able to fix that for RTM (DevDiv#1160769, DevDiv#1165732/Connect#1298009).
* bind()/function/etc. had difficulty with PMFs/PMDs (pointers to member functions/data). The Standard specifies that bind()/function/etc. call an imaginary function named INVOKE(), which does magical things (N4527 20.9.2 [func.require]/1). INVOKE() knows that function objects, PMFs, and PMDs all require different syntax, and it knows how to use PMFs/PMDs with references/raw pointers/smart pointers to base/derived objects. In C++17, this is a real function invoke() that you can call directly. My <functional> overhaul implemented invoke(), following the Standard exactly, and therefore fixed all of the bugs with PMFs/PMDs (DevDiv#294051/Connect#694400, DevDiv#477804/Connect#759806, DevDiv#487679/Connect#763571, DevDiv#505570/Connect#768903, DevDiv#505572/Connect#768899, DevDiv#789899/Connect#802299).
* bind() and std::function had several space inefficiencies, which could lead to dramatic time inefficiencies. An example was reported where storing a bound functor in a std::function worked fine on x86, but was almost 10x slower on x64 (DevDiv#490878). This was triggered by x64’s larger pointers exceeding the limit of our Small Functor Optimization (SFO), assisted by unexpected bloat in bind() and std::function’s representations. Fixing this involved several things. First, I reworked std::function’s representation, eliminating unnecessary bloat and making more space available for the SFO. Second, I retuned the SFO, which previously had a very small limit. The limit is officially undocumented and we reserve the right to change it in the future, but I can tell you what it is. In VS 2015, we consider a function object to be “small” (and can therefore avoid dynamic memory allocations) if it’s as big as a basic_string or smaller. (James McNellis suggested this heuristic.) Our basic_string is fairly large due to the Small String Optimization, so this means that functors can store several integers/pointers while remaining within the SFO. Finally, as part of an STL-wide overhaul to use compressed pairs, bind() compresses empty functors (like stateless lambdas or less<>) and std::function compresses empty allocators (like std::allocator, the default). This avoids wasting the SFO’s precious bits on storing empty classes. With all of these changes, we expect the SFO to be activated much more frequently. It’ll still be possible for 64-bit architectures to push user functors over the limit, but far fewer functors will be affected.
There are minor limitations, which I’ll mention for completeness. First, std::function doesn’t attempt to optimize empty functors with stateful allocators. (This doesn’t matter in practice, because a stateful allocator would have to be just below the SFO’s limit in order to be affected.) Second, bind() doesn’t attempt to optimize stateful functors bound to zero arguments. (This is extremely unlikely to matter in practice, because the only reason to bind zero arguments would be for nested bind(). bind(pmf/pmd) isn’t a reason, because that’s better written with mem_fn() or a stateless lambda.) Finally, bind() stores bound arguments in a tuple, which isn’t compressed, so binding placeholders or other empty arguments will pay 1 byte per argument. (This isn’t a big deal because the size of a bound functor rarely matters except when it exceeds the SFO’s limit, which we’ve dramatically increased. We may compress tuple in the future.)
* function::swap() is required to be noexcept. To achieve this, I’ve given the SFO an additional requirement. A functor must be small and is_nothrow_move_constructible must be true in order to activate the SFO.
* std::function was generating larger object/executable files than necessary, due to how the SFO was metaprogrammed (DevDiv#1174895). I reworked this, reducing the size of an artificial test case by 12% on x86 and 30% on x64. This was reported by the Office team, and the fix reduced the size of their x64 executable by 9% (they had many std::functions).
* The constructor function(F f) is required to store move(f), but we were copying it (DevDiv#759096). While I’ve fixed this, note that the Standard still requires F to be CopyConstructible, so you can’t store movable-only functors in std::function. (Usually, STL classes have “on-demand” requirements. For example, list<T> doesn’t require T to be less-than comparable, until you call list<T>::sort(), thanks to how templates work. std::function behaves differently due to type erasure. When you construct function<Signature> from UserFunctor, the std::function needs to generate all the operations on UserFunctor that could possibly be called, because its actual type is about to be erased through the magic of virtual functions, regardless of whether those operations will ultimately be needed by the whole program. Because std::function is CopyConstructible, it must require UserFunctor to be CopyConstructible, regardless of whether you actually copy any std::functions.)
* function<void (Args)> is now required to accept functors with non-void return types (LWG 2420, DevDiv#1010027/Connect#949899). If the functor returns anything, it will be ignored.
* For certain types (with long mangled names, like set<wstring>), function<Ret (Args)> emitted warning C4503 “decorated name length exceeded, name was truncated” (DevDiv#1053579, DevDiv#1094949/Connect#1052543). This was extremely annoying, although it didn’t affect runtime behavior. During VS 2015’s development, this problem temporarily became worse, while I was reworking std::function to properly use allocators. I tracked down the ultimate causes of C4503 and mitigated them as much as possible, so it’ll be very difficult to trigger unintentionally. (C4503 can’t be completely avoided with the current name mangling system. The compiler team may change this in the future.)
* reference_wrapper had trouble with function objects that defined result_type (DevDiv#794227), especially function objects that were simultaneously unary and binary (DevDiv#864867).
* reference_wrapper had trouble with function types, like reference_wrapper<int (int)>, where get() and assignment wouldn’t compile (DevDiv#535636, DevDiv#868374).
* reference_wrapper<AbstractBase> didn’t work with pure virtual function call operators (DevDiv#391117/Connect#734305, DevDiv#897806/Connect#828696).
* Everything in <functional> (except for the deprecated-in-C++11, removed-in-C++17 stuff like ptr_fun()) now respects arbitrary calling conventions, including __vectorcall, and the compiler options to change the default calling convention (/Gd, /Gr, /Gv, /Gz). Unlike Boost, this is always supported, so you don’t need to define macros to enable it. (DevDiv#553067/Connect#774720, DevDiv#563620/Connect#775554, DevDiv#793009/Connect#804357)
I rewrote call_once() and its associated once_flag from scratch, using Vista+’s InitOnceExecuteOnce() with a handwritten fallback for XP. This fixed all known bugs:
* Exceptional executions weren’t handled as required by the Standard (DevDiv#637433/Connect#781049, DevDiv#1086953/Connect#1038276).
* once_flag’s constructor wasn’t marked as constexpr as required by the Standard (DevDiv#497946). (Note that while it’s marked as constexpr in VS 2015 RTM, it’s affected by the compiler bug DevDiv#1134662 “constexpr constructors are emitting dynamic initializers”, which we’re planning to fix in 2015 Update 1.)
* call_once() previously used a global lock, which led to hangs in certain situations (DevDiv#840791/Connect#811192). This global lock also prevented separate call_once() invocations with separate once_flags from executing simultaneously (DevDiv#1092852).
* call_once() previously used bind(), which it wasn’t supposed to do. (bind() has special cases for placeholders, reference_wrappers, and nested bind() expressions – none of which should be treated specially by call_once().)
* The new implementation is much more efficient. I measured call_once()’s performance on my dev box (4-core 8-thread Sandy Bridge i7-2600 3.4 GHz; Server 2008 R2, Win7-class) by having 8 threads hammer a single once_flag with an empty lambda. To make the test take approximately one second, I needed 1M repeats per thread for VS 2013, and 35M for VS 2015. This allowed me to measure call_once()’s overhead very precisely. (For example, if it takes 1 s of wall clock time for 8 threads to perform 1M calls each, then I say that each call took 1 s / 8M = 125 ns.) Compared to VS 2013, I observe that VS 2015 x86 is 37.2x faster (212.0 ns improved to 5.7 ns), and x64 is 87.7x faster (306.9 ns improved to 3.5 ns). I believe that the difference between x86’s 5.7 ns and x64’s 3.5 ns is due to x64’s zero-overhead table-based exception handling (we need a try/catch block to transport exceptions, which can’t be allowed to rampage through InitOnceExecuteOnce()).
* atomic<T> now static_asserts that T must be trivially copyable (DevDiv#798735/Connect#805015). For example, atomic<string> will emit a compiler error.
* The Standard forgot to say so (tracked by the open issue LWG 2426), but the compare_exchange function family is supposed to read from “expected” before performing the atomic operation, and not after (DevDiv#1015776). This permits certain algorithms to be written, e.g. manipulating list nodes.
* shared_ptr’s atomic_compare_exchange function family could deadlock in certain situations (DevDiv#1066589/Connect#1004269). We fixed this, in addition to other subtle correctness and performance problems.
* <atomic> now supports the /clr compiler option (DevDiv#1088751/Connect#1041602). Note that the Clause 30 multithreading headers (<thread>, <future>, etc.) remain blocked; we’ll investigate lifting that restriction in the future.
Multithreading Fixes (ConcRT Removal)
We’ve reimplemented the STL’s multithreading primitives to avoid using the Concurrency Runtime (ConcRT). Using ConcRT was a good idea at the time (2012), but it proved to be more trouble than it was worth. Now we’re using the Windows API directly, which has fixed many bugs. (The only exception is that ConcRT is still used for XP targeting, so XP hasn’t received these fixes.)
* Using a mutex while constructing a global variable could hang (DevDiv#789979/Connect#802344, DevDiv#968936/Connect#900741).
* async()/mutex/etc. would crash when operator new/delete had been replaced (DevDiv#943284/Connect#868994, DevDiv#1019418/Connect#962406).
* timed_mutex::try_lock_for()/etc. leaked memory (DevDiv#957609/Connect#886682).
* condition_variable improperly handled zero timeouts (DevDiv#1082065/Connect#1029842).
Multithreading Fixes (Other)
* When constructing a std::thread, the Standard requires that “If the invocation of [the user’s callable object] terminates with an uncaught exception, std::terminate shall be called.” (N4527 220.127.116.11 [thread.thread.constr]/5). We previously enforced this with try/catch, but that made debugging difficult (DevDiv#918396/Connect#845184). Now we enforce this by allowing user exceptions to slam into noexcept, which is friendly to debugging.
* condition_variable’s predicate-wait functions were calling their predicates too often (DevDiv#1114006/Connect#1098841). Now they’re following the Standard exactly.
* std::thread’s constructor didn’t compile with movable-only arguments (DevDiv#377755/Connect#729886, DevDiv#1079684/Connect#1026715).
* this_thread::get_id() was unnecessarily slow (DevDiv#1039430).
* We’ve fixed all of the correctness issues reported by Anthony Williams (DevDiv#482769), except for “the std::atomic<> class template cannot be used on types without a default constructor” which is tracked by the open issue LWG 2334. A couple of the performance issues remain to be investigated.
* When called after main() exits (e.g. in a global destructor), thread::join() could hang (DevDiv#435439/Connect#747145).
* packaged_task::make_ready_at_thread_exit() could hang when called more than once (DevDiv#803317).
* packaged_task::reset() crashed when called on a packaged_task with no shared state (DevDiv#865726). Now it throws future_error with future_errc::no_state as required by the Standard.
* std::thread didn’t work with DLLs properly; it was possible for a DLL to be unloaded while a thread was still executing code in that DLL (DevDiv#895753). This was especially problematic in Windows Store apps.
* During VS 2015’s development, timed_mutex/recursive_timed_mutex’s try_lock_for()/try_lock_until() would spin a CPU core at 100% (DevDiv#1114242/Connect#1099043). They’ve been rewritten to properly block.
* When timing out, condition_variable’s wait_for()/etc. could wake up slightly too early (DevDiv#1129370).
In addition to implementing the N4100 Filesystem “V3” Technical Specification, we fixed several bugs. (Note that while we’re providing the TS’s header <experimental/filesystem> and namespace std::experimental::filesystem, we’re also providing the old header <filesystem> and namespace std::tr2::sys for limited back-compat.)
* The current_path() getter previously ignored failures (DevDiv#430113). We now throw an exception or report an error_code as appropriate. (In particular, this isn’t supported for Windows Store apps, so it’ll fail there.)
* path::generic_string()/etc. wasn’t returning forward slashes (DevDiv#982173/Connect#916517).
* Using the compiler option /Zc:wchar_t- with the filesystem library triggered linker errors (DevDiv#1004799). /Zc:wchar_t- is an abomination, but we grudgingly support it.
* rename(“test.txt”, “test.txt”) is required to be a no-op, but we were deleting the file (DevDiv#1066931/Connect#1006361).
* The last_write_time() getters and setters were truncating timestamps to whole seconds (DevDiv#1075324/Connect#1018797). Now we’re preserving Windows’ 100 ns resolution.
* The filesystem library has “throwers” like “bool create_directories(const path&)” and “non-throwers” like “bool create_directories(const path&, error_code&) noexcept”. Several non-throwers were mistakenly implemented by calling throwers without any try/catch logic (DevDiv#1180290/Connect#1385031). This would lead to exceptions slamming into noexcept and triggering program termination. We’ve fixed all known occurrences of this problem by having the non-throwers call other non-throwers. In one case, the thrower is the core implementation, and the non-thrower wraps it in try/catch. (Note that there are several remaining occurrences of non-throwers calling throwers, but they’re innocuous because the “throwers” involved can’t actually fail in our implementation. We may rework this in the future, but there was no time for VS 2015 RTM, as this was literally the last STL fix checked in.)
* copy_file(src_path, dest_path, copy_options::update_existing) (optionally with an error_code) is a request to copy/overwrite src_path to dest_path if and only if src_path is more recently modified (i.e. “newer overwrites older”). Due to a reversed timestamp test, our implementation believed that update_existing meant “older overwrites newer”.
* last_write_time(path, code), remove_all(path, code), and temp_directory_path(code) had incorrect return values for failure.
* remove_all() previously returned either 0 or 1. Now it correctly returns the number of files removed.
* shared_ptr/weak_ptr’s visualizers now display the original pointer stored in the control block (DevDiv#497336). For example, this matters when shared_ptr<Derived> is converted to shared_ptr<Base>, or shared_ptr<Anything> is converted to shared_ptr<void>. The control block remembers the original Derived * or Anything *.
* We now visualize exception_ptr (DevDiv#716887). This required magical custom machinery, implemented by Lukasz Chodorski and Eric Feiveson.
* We now visualize atomic and atomic_flag (DevDiv#756373/Connect#796725).
* reverse_iterator’s visualizer was technically correct but endlessly confusing (DevDiv#950042/Connect#879130). Now it has special cases for vector/array/list iterators and raw pointers. Also, the general case now displays the “current” data member as “base()” (which is the member function to retrieve it) and has a Synthetic child explaining “NOTE: *ri is equivalent to *prev(ri.base())”. This should prevent further confusion.
* We now visualize recursive_mutex (DevDiv#992590/Connect#926641).
* We now visualize future and promise (DevDiv#1014393).
* We now visualize initializer_list and chrono::duration.
* The visualizers for tree iterators (map/multimap/set/multiset) now detect end iterators.
* mt19937_64 is supposed to take 64-bit seeds, but it was truncating them to 32-bit (DevDiv#619912/Connect#779231).
* The output of piecewise_linear_distribution was completely incorrect (DevDiv#665556/Connect#783628).
* The output of generate_canonical() was completely incorrect (DevDiv#844616/Connect#811611).
* The output of binomial_distribution was incorrect for some parameters like (100, 0.991) (DevDiv#940047, DevDiv#1188781/Connect#1444620).
* Distributions and their param_types are supposed to have matching constructors including explicitness, but many differed (DevDiv#977224, DevDiv#1076782/Connect#1021566).
* put_time() incorrectly treated %z (time zone offset) as a synonym of %Z (time zone name/abbreviation) (DevDiv#821666/Connect#808156). This was actually a bug in the CRT’s strftime(), which was fixed by James McNellis.
* Parsing floating-point with iostream’s operator>>() had inaccurate results (DevDiv#938627/Connect#866093, DevDiv#961116/Connect#890195). We’ve substantially improved the correctness here, although we’re still getting a few corner cases wrong, and there are significant performance issues. In the next major version, we’re planning to switch back to the CRT for floating-point parsing, which has been overhauled for complete correctness (and is much faster than iostream’s separate codepaths). Note that in VS 2015, the stod()/to_string() family has been rewritten to use the CRT as required by the Standard.
* We fixed a couple of read overruns in iostream’s floating-point and integer parsing (DevDiv#900436/Connect#829931, DevDiv#911845/Connect#840103).
* collate::transform() misbehaved when given inputs consisting entirely of null characters, throwing an exception on x86 and allocating a huge string on x64 (DevDiv#869525/Connect#814337).
* basic_istream::ignore(n, delim) was comparing n to INT_MAX, instead of numeric_limits<streamsize>::max() as required by the Standard (DevDiv#964171/Connect#894605). This was a behavioral problem, not cosmetic (we have 32-bit int and 64-bit streamsize).
* time_get::do_get_year() thought that the world will end in 2035 (DevDiv#640278). Old behavior:
- [0, 135] parsed as [1900, 2035]
- [136, 1899] set failbit
- [1900, 2035] parsed literally
- [2036, 9999] set failbit
- [0, 68] parsed as [2000, 2068]
- [69, 99] parsed as [1969, 1999]
- [100, 9999] parsed literally
* Additionally, time_get::do_get_year() wasn’t following N4527 22.4 [locale.categories]/2 “The get() members take an ios_base::iostate& argument whose value they ignore, but set to ios_base::failbit in case of a parse error.” (DevDiv#990695), because it wasn’t ignoring the incoming value.
* We fixed an inconsistency in our internal _Yarn machinery that affected the Intel compiler (DevDiv#879860/Connect#817221). (It’s named that way because yarn is similar to string and cats love yarn.)
* system_category().default_error_condition() was thoroughly incorrect, affecting most uses of system_category() (DevDiv#781294/Connect#800821). Given a Windows error code, if its value happened to be a valid Posix error code value (but not necessarily the same meaning – typically a totally different meaning!), it would return that value tagged with generic_category(). Now we’re implementing N4527 18.104.22.168 [syserr.errcat.objects]/4 properly: “If the argument ev corresponds to a POSIX errno value posv, the function shall return error_condition(posv, generic_category()). Otherwise, the function shall return error_condition(ev, system_category()). What constitutes correspondence for any given operating system is unspecified.”
* Error category objects didn’t behave properly across different DLLs/EXEs (DevDiv#666062, DevDiv#1095970/Connect#1053790). The tale of woe here was complicated. Calling generic_category(), for example, is supposed to return a reference to a single unique object, regardless of where it’s called. This is usually achieved by separate compilation into the STL’s DLL (or static LIB). However, we can’t separately compile error_category machinery, because it has a virtual message() returning std::string, whose representation is affected by _ITERATOR_DEBUG_LEVEL. So, generic_category() is implemented header-only – but that means that different user DLLs end up with different instantiations and therefore different objects. (It’s also possible for this to cause trouble between a user’s EXE and the STL’s DLL.) We fixed this to achieve as much conformance as possible. We’ve taught error_category, its derived classes, and its operator==()/operator!=() to consider all generic_category() objects to be equal, even if they live at different addresses in different DLLs (and similarly for the other error category objects in the Standard). This has been implemented so that user-defined error category objects will be unaffected. The only thing we can’t fix is a direct comparison of error_category addresses (code should use operator==() instead).
* system_category().message() now uses FormatMessage() to stringize Windows error codes (DevDiv#1101599/Connect#1075847). This provides more detailed messages for many more error codes than the table we previously used.
* The map/set family rejected empty lambdas as comparators (DevDiv#375529/Connect#727957). This was an example of an STL-wide problem – we attempted to optimize away empty comparators/allocators/etc., but did so inconsistently and incorrectly (e.g. by assuming that empty objects must be default constructible, which is false). unique_ptr and unordered_meow also had trouble with deriving from user-defined deleters/hashers (e.g. unordered_meow was callable like a functor). I fixed this by implementing a compressed pair with the Empty Base Class Optimization, then overhauling the STL to use it. This centralized compressed pair handles everything correctly – e.g. it doesn’t assume default constructible types, it respects final classes, and it must be used as a data member (preventing the unique_ptr/unordered_meow problems). Here’s an exhaustive list of what was updated:
- Sequence containers: deque, forward_list, list, vector (empty allocators)
- Associative containers: map/etc. (empty comparators, empty allocators)
- Unordered containers: unordered_map/etc. (empty hashers, empty equality predicates, empty allocators)
- <functional>: bind() (empty callable objects)
- <functional>: function (empty allocators)
- <memory>: shared_ptr, allocate_shared() (empty deleters, empty allocators)
- <memory>: unique_ptr (empty deleters)
- <string>: basic_string (empty allocators)
* The compressed pair overhaul also fixed a compiler error when attempting to convert-move-construct unique_ptrs with custom deleters (DevDiv#1076756/Connect#1021477).
* unordered_meow’s a.rehash(n) didn’t quite achieve N4527 23.2.5 [unord.req]’s postcondition “a.bucket_count() > a.size() / a.max_load_factor() and a.bucket_count() >= n” (DevDiv#824596).
* unordered_meow insertion was invoking the equality predicate too often, which harmed performance slightly.
* vector’s insert(const_iterator, const T&) and insert(const_iterator, size_type, const T&) were copying instead of moving elements around (DevDiv#824985).
* list::sort() and forward_list::sort() assumed that they could default-construct allocators, which isn’t guaranteed (DevDiv#920385). I fixed this by rewriting them from scratch, so they don’t attempt to allocate any memory now.
* The STL was always requiring allocators to be assignable (DevDiv#1119194/Connect#1114355). Now we’re correctly following N4527 22.214.171.124 [allocator.requirements]/4, which requires allocators to be CopyAssignable/MoveAssignable/swappable when POCCA/POCMA/POCS are true.
“Fools!”, hissed the Dark Lord. “You have entered the realm of allocators, where I have mastery over the lifetimes of all things!” Then he chanted horrifying words of power: “Pocca, pocma, pocs…”
* <algorithm>’s search() eagerly called distance(), which wasn’t very friendly to weaker-than-random iterators (DevDiv#1003120/Connect#940497). Now we have separate implementations for random and weaker iterators.
* stable_sort() performed self-move-assignments, which aren’t required to be tolerated by elements (DevDiv#957501/Connect#886652).
* regex_match()/regex_search() weren’t correctly setting m.prefix().matched/m.suffix().matched (DevDiv#903531).
* basic_regex’s copy constructor wasn’t thread-safe (DevDiv#1158803/Connect#1253646).
* is_assignable didn’t tolerate overloaded comma operators (DevDiv#938759).
* is_trivially_copyable returned incorrect answers (DevDiv#807340/Connect#806233, DevDiv#940515/Connect#868824). We had mistakenly implemented it as a synonym of is_trivially_copy_constructible. Now, is_trivially_copyable is implemented with a compiler hook, which has been fixed to return correct answers.
* <string> now provides proper u16string/u32string typedefs (DevDiv#1078492/Connect#1023646).
* The stod() family mishandled INF/NAN inputs by throwing exceptions (DevDiv#1113936/Connect#1098595). We’re now following the Standards exactly.
* bitset’s constructor was validating characters, but not following N4527 20.6.1 [bitset.cons]/5 exactly (DevDiv#931383).
* pair/tuple didn’t have defaulted move constructors as required by the Standard (DevDiv#961569/Connect#891428).
* The STL now tolerates _USE_32BIT_TIME_T, but that option is still incredibly evil (DevDiv#972321/Connect#904065, DevDiv#1026777/Connect#972033).
* The option _HAS_EXCEPTIONS=0 is undocumented, untested, and unsupported by Microsoft. We pick it up from Dinkumware’s master sources and try not to mess with it. At our discretion, we’re occasionally willing to fix major problems with this option. The bug report DevDiv#1073766/Connect#1015473 observed that 2015’s support for noexcept was emitting EH logic even for _HAS_EXCEPTIONS=0. We chose to fix this, so 2015’s _HAS_EXCEPTIONS=0 will behave like 2013’s did.
* By implementing constexpr, we’ve fixed all “missing constexpr” bugs (DevDiv#961568/Connect#891373, DevDiv#1074023/Connect#1015584, DevDiv#1148036/Connect#1211985), with a very small number of exceptions that are being tracked.
Between VS 2015 CTP1 and RTM, we implemented 23 C++14/17 Library Issues (which are fixes for bugs in the Standard itself):
- LWG 2009 Reporting out-of-bound values on numeric string conversions
- LWG 2094 duration conversion overflow shouldn’t participate in overload resolution
- LWG 2103 std::allocator_traits<std::allocator<T>>::propagate_on_container_move_assignment
- LWG 2275 Why is forward_as_tuple not constexpr?
- LWG 2280 begin / end for arrays should be constexpr and noexcept
- LWG 2301 Why is std::tie not constexpr?
- LWG 2129 User specializations of std::initializer_list
- LWG 2212 tuple_size for const pair request header
- LWG 2365 Missing noexcept in shared_ptr::shared_ptr(nullptr_t)
- LWG 2399 shared_ptr’s constructor from unique_ptr should be constrained
- LWG 2400 shared_ptr’s get_deleter() should use addressof()
- LWG 2401 std::function needs more noexcept
- LWG 2403 stof() should call strtof() and wcstof()
- LWG 2407 packaged_task(allocator_arg_t, const Allocator&, F&&) should neither be constrained nor explicit
- LWG 2420 function<void(ArgTypes…)> does not discard the return value of the target object
- LWG 2433 uninitialized_copy()/etc. should tolerate overloaded operator&
- LWG 2440 seed_seq::size() should be noexcept
- LWG 2442 call_once() shouldn’t DECAY_COPY()
- LWG 2454 Add raw_storage_iterator::base() member
- LWG 2458 N3778 and new library deallocation signatures
- LWG 2464 try_emplace and insert_or_assign misspecified
- LWG 2467 is_always_equal has slightly inconsistent default
- LWG 2488 Placeholders should be allowed and encouraged to be constexpr
Jonathan Caves implemented LWG 2129 enforcement in the compiler, because the STL by itself couldn’t prevent users from explicitly/partially specializing initializer_list.
I’m keeping track of all C++14/17 Library Issues in a table, but it’s grown really big (210 rows), so I’ll just summarize it. 109 issues are N/A (nothing for an implementer to do). 32 issues were implemented in VS 2013 (and possibly earlier). 47 issues were newly implemented in VS 2015 (24 listed last year plus 23 listed here). 22 issues remain to be implemented.
* During TR1/C++0x’s evolution, many type traits went through name changes before being finalized in C++11. In VS 2013, we supported both the old names and the new names. Although the old names were synonyms for the new names (so we didn’t have divergent implementations), this was undesirable for several reasons. First, it created a confusing mess of names. Second, it encouraged non-Standard code to be written, for no benefit whatsoever. Finally, it embiggened <type_traits> and reduced compiler throughput, however slightly. So in VS 2015, we’ve removed the old names. If you were using them, you’ll have to change your code to use the new names. (They’re synonymous, so there’s no behavioral impact.)
Here’s the mapping from Old Name (TR1/C++0x) ==> New Name (C++11 and beyond):
- add_reference ==> add_lvalue_reference
- has_default_constructor ==> is_default_constructible
- has_copy_constructor ==> is_copy_constructible
- has_move_constructor ==> is_move_constructible
- has_copy_assign ==> is_copy_assignable
- has_move_assign ==> is_move_assignable
- has_nothrow_constructor ==> is_nothrow_default_constructible
- has_nothrow_default_constructor ==> is_nothrow_default_constructible
- has_nothrow_copy ==> is_nothrow_copy_constructible
- has_nothrow_copy_constructor ==> is_nothrow_copy_constructible
- has_nothrow_move_constructor ==> is_nothrow_move_constructible
- has_nothrow_assign ==> is_nothrow_copy_assignable
- has_nothrow_copy_assign ==> is_nothrow_copy_assignable
- has_nothrow_move_assign ==> is_nothrow_move_assignable
- has_trivial_constructor ==> is_trivially_default_constructible
- has_trivial_default_constructor ==> is_trivially_default_constructible
- has_trivial_copy ==> is_trivially_copy_constructible
- has_trivial_copy_constructor ==> is_trivially_copy_constructible
- has_trivial_move_constructor ==> is_trivially_move_constructible
- has_trivial_assign ==> is_trivially_copy_assignable
- has_trivial_copy_assign ==> is_trivially_copy_assignable
- has_trivial_move_assign ==> is_trivially_move_assignable
- has_trivial_destructor ==> is_trivially_destructible
Note that throughout the STL, we’re still supporting std::tr1::MEOW as an alias of std::MEOW, for names that made it into the C++11 Standard unchanged. (For example, is_void.) This support will be removed in the future.
* In <future>, we’ve removed a couple of non-Standard flags. Here’s the mapping from non-Standard ==> Standard:
- launch::any ==> launch::async | launch::deferred
- launch::sync ==> launch::deferred
* In the Standard (and all versions of VS), priority_queue<T> has a “const T& top() const” member function. In VS 2015, we’ve removed a non-Standard “T& top()” overload. This non-const overload was eye-meltingly dangerous, because it could accidentally be used to violate the data structure’s invariants.
* After reimplementing timed_mutex/recursive_timed_mutex, we had to remove their native_handle_type/native_handle(), because they don’t directly wrap an underlying implementation anymore. Note that this is permitted by the Standard; see N4527 30.2.3 [thread.req.native]/1: “Several classes described in this Clause have members native_handle_type and native_handle. The presence of these members and their semantics is implementation-defined.”
Unless otherwise specified, these fixes were implemented by myself, P.J. Plauger (Dinkumware), Alex Voicu, Artur Laksberg, and Hong Hong.
Stephan T. Lavavej
Senior Developer – Visual C++ Libraries