0
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s;
    s.reserve(5);
    cout << s.capacity() << endl;
}

The reserve is a std::string's function that sets the capacity. The capacity function shows the space size of c_string in the std::string.

But, the result is not 5, but 15. I don't know why.

3088 K
  • 75
  • 5
  • 5
    Maybe Small string optimization? What c±± version and compiler are you using? – Quimby Nov 18 '22 at 07:44
  • 1
    why not ? What else did you expect and why? – 463035818_is_not_an_ai Nov 18 '22 at 07:49
  • 1
    std::string may have an optimization for short strings meaning that it always have at least some capacity even if you need less. I'm not sure whether this optimization is needed according to the standard. – stefaanv Nov 18 '22 at 07:49
  • 2
    `reserve` is not ignored. When capacity is larger then nothing needs to be reserved – 463035818_is_not_an_ai Nov 18 '22 at 07:49
  • and even when SSO is not used then the reserved size might not be the same as you expected but likely the next higher number in the allocator list. It almost never resizes to the exact size you propose – phuclv Nov 18 '22 at 07:50
  • A quick check found empty string has capacity 15 (probably SSO in gcc) and reserve with smaller number didn't decrease capacity, as mentioned in other comments. – stefaanv Nov 18 '22 at 07:57
  • `s.reserve()` is not required to *reduce* capacity of the vector, nor is it required to give exactly the requested capacity. Apparently, with your implementation (compiler and standard library) a default-constructed `std::string` has capacity `15` or the `reserve()` function can reserve more than requested. – Peter Nov 18 '22 at 08:52

2 Answers2

7

From https://en.cppreference.com/w/cpp/string/basic_string/reserve

If new_cap is greater than the current capacity(), new storage is allocated, and capacity() is made equal or greater than new_cap.

If new_cap is less than the current capacity(), this is a non-binding shrink request.

If new_cap is less than the current size(), this is a non-binding shrink-to-fit request equivalent to shrink_to_fit() (since C++11). (until C++20)

If new_cap is less than or equal to the current capacity(), there is no effect. (since C++20)

There is no guarantee that by calling reserve will leave you a capacity exactly as what you provided regardless of the C++ version you are using

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • 4
    since c++20 ["If new_cap is less than or equal to the current capacity(), there is no effect. "](https://en.cppreference.com/w/cpp/string/basic_string/reserve). Unfortunately cpluscplus is not up to date (and sometimes even contains wrong information). I prefer https://en.cppreference.com/ – 463035818_is_not_an_ai Nov 18 '22 at 07:54
  • cppref even has an example that shows the initial 15 (but no text to explain it) – 463035818_is_not_an_ai Nov 18 '22 at 07:55
  • [cplusplus.com is very much frowned upon here](https://stackoverflow.com/q/6520052/995714) because it contains wrong information. https://en.cppreference.com/ is the authoritative source – phuclv Nov 18 '22 at 13:12
  • Actually both gives very similar information in this context. Anyway let me change my quote to cppreference.com – Adrian Shum Nov 21 '22 at 02:15
2

Note that also

int main()
{
    std::string s;
    std::cout << s.capacity() << "\n";
    s.reserve(5);
    std::cout << s.capacity() << "\n";
}

Would print 15 twice.

Consider the output of this

#include <iostream>
#include <string>

int main()
{
    std::string s;
    std::cout << s.capacity() << "\n";
    std::cout << sizeof(std::string) << "\n";
    std::cout << sizeof(char) << "\n";
    std::cout << sizeof(char*) << "\n";
    std::cout << sizeof(size_t) << "\n";
}

Possible output:

15
32
1
8
8

A std::string somehow has to store the character array, its size, capacity and perhaps some more bookkeeping. The obvious way to store a character array is with a char* and a size_t for the size (only null-terminator is not sufficient for constant time size() and others). However, thats 16 bytes. A single character is only 1 byte. Hence an empty std::string before allocating any dynamic memory has enough space to store some characters by reusing memory that is used for other stuff once the string grows.

Thats is short string optimization. I only outlined the general idea. For details I refer you to other resources or the implementation.

Further reserve(n) will only make sure that the string has enough space for at least n characters. An empty string already has enough space to store 5 characters. When n is smaller than the current capacity the string might shrink or not (it does not since C++20).

TL;DR: Your call to reserve is not ignored. After the call the string has enough capacity to hold 5 characters. The initial capacity on your implementation seems to be 15. It could be some other value as well.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185