basic_string reserve and vector/unordered_map/unordered_set reserve functionsSection: 27.4.3.5 [string.capacity] Status: Resolved Submitter: Andrew Luo Opened: 2017-05-30 Last modified: 2020-09-06
Priority: 3
View all other issues in [string.capacity].
View all issues with Resolved status.
Discussion:
According to 27.4.3.5 [string.capacity] paragraph 11:
-11- Effects: After
reserve(),capacity()is greater or equal to the argument ofreserve. [Note: Callingreserve()with ares_argargument less thancapacity()is in effect a non-binding shrink request. A call withres_arg <= size()is in effect a non-binding shrink-to-fit request. — end note]
A call to basic_string's reserve function with res_arg <= size() is taken as a non-binding
request to shrink the capacity, whereas for vector (and similarly for unordered_map and unordered_set)
according to 23.3.13.3 [vector.capacity] p3:
-3- Effects: A directive that informs a
vectorof a planned change in size, so that it can manage the storage allocation accordingly. Afterreserve(),capacity()is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value ofcapacity()otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument ofreserve(). If an exception is thrown other than by the move constructor of a non-CopyInsertabletype, there are no effects.
The problem here is that the different behavior makes it that writing template code where the template argument type is a
container type (for example std::string or std::vector<char>) calls to reserve can have
different meaning depending on which container type the template is instantiated with. It might be a minor issue but
it would be nice to fix the inconsistency. I ran into an issue around this when I was porting code from MSVC++ to G++
(For basic_string, MSVC++'s STL implementation, based on Dinkumware, ignores the call if res_arg < capacity()
whereas GCC's STL implementation, libstdc++ will actually shrink the string. For the code I wrote this caused a huge
performance issue since we were reallocating the entire string with every call to reserve. Of course we could have
worked around it by doing the res_arg < capacity() check ourselves, but I think this inconsistency in the
standard isn't desirable).
-11- Effects: After
reserve(),capacity()is greater or equal to the argument ofreserveif reallocation happens; and equal to the previous value ofcapacity()otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument ofreserve().
I realize that this causes the basic_string::reserve to no longer have the secondary property of shrinking,
but this is what shrink_to_fit is for.
[2017-07 Toronto Monday issue prioritization]
Priority 3; status to LEWG
[2018-3-17 Resolved by P0966, which was adopted in Jacksonville.]
Proposed resolution:
This wording is relative to N4659.
Edit 27.4.3.5 [string.capacity] as indicated:
void reserve(size_type res_arg=0);-10- The member function
-11- Effects: Afterreserve()is a directive that informs abasic_stringobject of a planned change in size, so that it can manage the storage allocation accordingly.reserve(),capacity()is greater or equal to the argument ofreserve, if reallocation happens; and equal to the previous value ofcapacity()otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument ofreserve().[Note: Calling-12- Throws:reserve()with ares_argargument less thancapacity()is in effect a non-binding shrink request. A call withres_arg <= size()is in effect a non-binding shrink-to-fit request. — end note]length_errorifres_arg > max_size().