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_arg
argument 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.11.3 [vector.capacity] p3:
-3- Effects: A directive that informs a
vector
of 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-CopyInsertable
type, 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 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()
.
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_string
object 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_arg
argument 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_error
ifres_arg > max_size()
.