-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
@xiangfan-ms discovered that vector's move constructor zero-initializes its pointers before taking the other vector's pointers, and that this isn't optimized away in release mode. (Which results in larger, slower codegen, and probably inhibits further optimizations like memcpy coalescing.)
vector's move constructor constructs its base before allocating the proxy and taking the other's pointers:
Lines 581 to 584 in 530bdc5
vector(vector&& _Right) noexcept : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Move_construct(_Right, true_type{}); } - The base construction sets the pointers to null:
Line 375 in 530bdc5
_Vector_val() noexcept : _Myfirst(), _Mylast(), _Myend() {} - Then
_Move_constructcalls_Take_contentswhich re-assigns the pointers:
Lines 556 to 558 in 530bdc5
void _Move_construct(vector& _Right, true_type) noexcept { // move from _Right, stealing its contents _Mypair._Myval2._Take_contents(_Right._Mypair._Myval2); }
Lines 384 to 393 in 530bdc5
void _Take_contents(_Vector_val& _Right) noexcept { this->_Swap_proxy_and_iterators(_Right); _Myfirst = _Right._Myfirst; _Mylast = _Right._Mylast; _Myend = _Right._Myend; _Right._Myfirst = pointer(); _Right._Mylast = pointer(); _Right._Myend = pointer(); }
I believe it's possible to change this behavior. We'd need to make _Vector_val constructible from three pointers, so we can directly initialize them. (This will work with _Compressed_pair's one-then-variadic-args - the one arg is for the allocator, the variadic args are sent to _Vector_val.) Then we'd need to allocate/swap proxies (which vanishes for release mode), and null out the other pointers. (This is noexcept so allocation failure is termination - no EH concerns. vNext will remove the allocation.)