41

There are several constructors for std::string. I was looking for a way to avoid reallocation and I'm surprised that there is a fill constructor but no "reserve" constructor.

 std::string (size_t n, char c);

but no

 std::string (size_t n);

So do I have to call reserve() after it already allocated the default (16 bytes in my case), just to immediately reallocate it?

Is there a reason why there is no such constructor to reserve space directly when the object is created, instead of having to do it manually? Or am I missing something and there is some way to do this?

Using the fill constructor is a waste of time, because it will loop through the memory just to get overwritten, and also cause a wrong size, because s.length() reports N instead of 0.

edmz
  • 8,220
  • 2
  • 26
  • 45
Devolus
  • 21,661
  • 13
  • 66
  • 113
  • Are you sure 16 bytes are reserved per default? (`sizeof(std::string)` has nothing to do with that) – deviantfan Sep 23 '15 at 11:54
  • 4
    @deviantfan, that's implementation-defined – SingerOfTheFall Sep 23 '15 at 11:55
  • @SingerOfTheFall Yes, like the 16 byte too. I specifically meant Devolus implementation – deviantfan Sep 23 '15 at 11:56
  • i verified that yesterday with VS2010 (at least with the debug build it seems so). – Devolus Sep 23 '15 at 11:56
  • To avoid reallocation you might use `string::reserver` http://www.cplusplus.com/reference/string/string/reserve/ – Simon Kraemer Sep 23 '15 at 11:56
  • 4
    @SimonKraemer, I know that I can use reserve, but I wanted to specify the planned size when I create the object already and avoid allocating the (possible) default size first. – Devolus Sep 23 '15 at 11:57
  • 1
    it's not [guaranteed that reserve() is faster](http://stackoverflow.com/a/20174822/819272) – TemplateRex Sep 23 '15 at 12:00
  • So looking at the assembly it seems that std::string indeed doesn't preallocate in a release build, only in the debug version. – Devolus Sep 23 '15 at 12:08
  • 2
    @Devolus: Since you're talking about VS2010 it uses the [Small String Optimization](http://stackoverflow.com/questions/10315041/meaning-of-acronym-sso-in-the-context-of-stdstring) and the initial capacity isn't actually dynamically allocated. – Blastfurnace Sep 23 '15 at 12:08
  • Then the string is `uninited`, right? What's the expected value of such string? – luoluo Sep 23 '15 at 12:08
  • @Blastfurnace, Yes, you are right, but only in case of a relase build. In a debug build it does. – Devolus Sep 23 '15 at 12:08
  • @luoluo, the expected result is an empty string with enough memory which will be filled afterwards. – Devolus Sep 23 '15 at 12:09
  • @Devolus No it doesn't pre-allocate in a debug build. It never allocates any _buffer_ for the string on construction. It uses [SSO](http://stackoverflow.com/questions/10315041/meaning-of-acronym-sso-in-the-context-of-stdstring) both in debug and release builds. What makes you think it does? – Mike Vine Sep 23 '15 at 12:17
  • @Devolus I thought about it and I think that it is "missing" to avoid confusion. The fill-constructor changes the `used size` while a reserve-constructor would only change the `reserved size`. All constructors taking a size value use it for the `used size`. – Simon Kraemer Sep 23 '15 at 12:19
  • @MikeVine, the debug break after setting a breakpoint in malloc before I created an empty string object on the stack. ;) At least in VS2010 it definitly mallocs in debug. I looked at the assemblycode for the releasebuild and there it doesn't happen (also doesn't trigger a breakpoint in release) as verfied with the debugger. – Devolus Sep 23 '15 at 13:07
  • @Devolus IIRC, the allocation is for MSVC's *checked iterators*, that is, a purely debug feature. It is not preallocating for the character contents of the string. – dyp Sep 23 '15 at 13:11
  • @dyp, when I do the accepted answer, then no BP is hit. Shouldn't this happen in this case as well? – Devolus Sep 23 '15 at 13:13
  • @Devolus No breakpoint for what? What is your test case? What do you break on? (memory allocations?) – dyp Sep 23 '15 at 13:27
  • @Devolus No, what you are doing is measuring something which is implementation defined. VS can choose when and how to allocate its proxy objects. Take a look at the callstack when the malloc is hit - if its a 'proxy' object or debug iterator list then its got nothing to do with allocating the strings buffer. – Mike Vine Sep 23 '15 at 13:27
  • You may need to right-click -> show external code to see the real call stack. – Mike Vine Sep 23 '15 at 13:34

3 Answers3

9

This is all guesswork, but I'll try.

If you already know the size of the string that you need, you will most likely be copying data from somewhere else, e.g. from another string. In that case, you can call one of the constructors that accept char * or const std::string & to copy the data immediately.

Also, I can't see why using reserve right after constructing a string is a bad thing. While it is implementation-defined, I would assume that it would make sense for this code:

std::string str;
str.reserve(100);

to allocate memory for a total of 100 elements, not 116 (as in "allocate 16 first, then free them and allocate 100 more"), thus having no performance impact over the non-existent reserve constructor.

Also, if you just want an empty string without the default allocation at all, you can presumably use std::string str(0, ' '); which invalidates the "Using the fill constructor is a waste of time" point.

SingerOfTheFall
  • 29,228
  • 8
  • 68
  • 105
  • 2
    That `std::string str(0, ' ');` indeed prevented the default allocation (VS2010). I verfied that this just happens in a debug build, so it wouldn't be a problem anymore, but anyway, this was what I was looking for. – Devolus Sep 23 '15 at 13:05
  • 1
    When `std::string` provides you some initial capacity from the default constructor, it is because they just come built-in with some stack space. There is nothing special to allocate and deallocate. – David Stone May 05 '16 at 14:04
  • Why would you use `std::string str(0, ' ')` and not `std::string str(0, 0 /* or '\0' */ )` ? – H-005 Aug 27 '20 at 13:12
  • `std::string str(0, ' ')` doesn't seem to be working, at least not on the compiler used on onlinegdb; checking `str.capacity()` still returns 15. – Setsu Oct 14 '22 at 15:53
0

One of the use cases for reserve constructor might be using std::string with static/thread_local storage for cache variables.

void function(){
   thread_local std::string str; // no reserving constructor defined
}

For such scenarios, you may write a lambda function which constructs a std::string and also reserves desired amount of memory.

void function(){
    auto reserving_string_constr = [](std::size_t reserve_size){
        std::string str;
        str.reserve(reserve_size);
        return str;
    };
    thread_local std::string str(reserving_string_constr(128));
    /*...*/
}
-3

you can basically ask the same question about every std::string method that is not embodied as a constructor.

for example, why don't we have a constructor which takes multiple strings and appends them one by one to the newly created string?

there can be many reasons why not to overload a constructor which reserve a memory. I think that in an Object Oriented setmind, the thought of a "reserved memory string" is a bit odd. the string represents a sequence of characters, not the memory behind it - which is implementation details, not the main feature.
when someone opens a new resturant, does he think to himself "when this resturant is finally open, on the very moment of opening I will reserve 100 sits for the alleged 100-people group that may or may not come!"

but your question implicitly states something very quitly: the lack of proper buffer objects in C++. you ask about reserve constructor because you want to use string as buffer, but zero-initializing them is very costly. reserveing the memory won't let you write beyond the string size.
so the solution to this problem is to use unique_ptr<char[]> which will not zero -initialize the characters, yet will give you the RAII style you are looking for.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • I don't see how a unique_ptr object would help here because it doesn't give me the functionality of std::string. – Devolus Sep 23 '15 at 13:01
  • what functionality for example? – David Haim Sep 23 '15 at 13:06
  • For example: str += txt; – Devolus Sep 23 '15 at 13:09
  • then use `std::copy`. there is no magical way here, either use `std::string` ctor. + `reserve` or use other ways to achieve that , like using `char[]` with `std::copy` + keeping the index. – David Haim Sep 23 '15 at 13:11
  • Then why are there reserve constructors for other things such as `std::vector`? The idea is that sometimes you know how long the string would be, and if there is a constructor like this: `string(size_t n, char x)`, why wouldn't there also be a constructor like this: `string(size_t)`? – H-005 Aug 27 '20 at 13:19