Immediately make a reservation, the question is purely theoretical, without practical application. 3 code examples:

one)

std::string s = "abc"; char& c = s[0]; c = 'A'; // OK, s == "Abc" 

2)

 std::string s = "abc"; char& c = s[0]; s += "def"; c = 'A'; // Error: UB, s+="def" 'kill' c reference 

3)

 std::string s = "abc"; s.reserve(100); char& c = s[0]; s += "def"; c = 'A'; // ??? OK (s == "Abcdef") or UB??? 

Quote from Josattis "Standard C ++ Library. Reference Guide":

In order to avoid mistakes ... you should reserve enough capacity before the link is initialized ...

Actually, the question is, from the point of view of the C ++ 11 standard. Is the 3rd sample code correct? (from the point of view of style - bad, I understand).

  • As I understand it, it is correct, but of course you need to find the appropriate part of the standard. - VladD 2:47 pm

4 answers 4

According to clause 21.3.1.1 / 4 of the draft Standard, calling a non-const member function other than operator[] , at , front , back , begin , rbegin , end and rend can lead to invalidation of references / pointers / iterators to string objects (characters) . In this case, the modifying operator+= called.

So Disability (and as a result, UB) is possible despite the presence of the reserve call.

  • and what is the standard required to make a reserve call? Anything at the discretion of the implementation? - pavel
  • @pavel reserve increases capacity , or decreases (but this is not necessary). At the same time, it seems to me that the point about disability should include the condition that if the modification did not lead to a redistribution of memory (ie, the capacity did not change), then there should not be any invalidation. It seems like we are striving for this when we reserve in advance the memory. However, it is not written in the Standard. And there is an unequivocal mention of what I wrote in the answer. Those. disability is possible. - αλεχολυτ
  • The standard says that += has the same effect as append . The documentation on append says "replacing the string with a string of length size() + n ", that is, about capacity nothing. - VladD 5:19 pm
  • @alexolut, in the standard everything is correctly written, what you mention would lead to a restriction of the implementation. For example, if we introduce such a condition, then it will not be possible to make it so that when reserve no memory allocation takes place, and it would occur only when trying to add an element. - ixSci
  • one
    @alexolut, yes, reserve must provide capacity , but there is not a word about memory. Therefore, we can virtually “allocate” memory, i.e. to return the increased capacity , but at the same time not to allocate a new memory, but to allocate it only when it “presses”. How useful is this? I have no idea, it may be generally useless, and maybe it will be useful - the point is that the standard tries to give maximum freedom in the implementation of entities, while giving some guarantees. reserve is a typical optimization hint, which should not be pinned high hopes, except for one - the potential minimization of the allocation ... - ixSci

And why should it be incorrect? The question needs to be put differently: why does UB happen in the second case? And it happens because the buffer, in the character in which the reference c points, can be destroyed when the string is expanded. In the third case, the buffer remains the same (at least as long as the new line length does not exceed its size).

  • one
    Just on the 2nd case there are no questions, classic UB, since depends on implementation. It can work, it can crash the program. And according to the 3rd example, the question is whether the standard guarantees the safety of references to line elements when the line is changed, provided that the buffer is sufficient. - andy.37 2:46 pm
  • The standard does not agree with you. - αλεχολυτ 5:03 pm

The second and third cases are absolutely equivalent, from the point of view of the standard, both of them can give rise to UB. From a practical point of view, none of these cases will lead to UB, on any of the popular implementations of the standard library.

  • Why the second option can not lead to UB in practice? I think it can, because due to the optimization of short strings before the call += , the internal string buffer can be used, and after that - heap memory allocation will occur, and as a result the links will be broken. - αλεχολυτ pm
  • I can believe about the third, but I do not understand why there are no problems for the second. After all, the buffer can be relocated? Or do you refer to the details of the exact buffer relocation strategy? - VladD pm
  • @alexolut, because the SSO buffer is clearly greater than 6. - ixSci pm
  • @VladD, because SSO (Small string optimization) is used now everywhere - ixSci
  • 2
    Maybe for specific row sizes from the question and will not lead, but this is walking on very thin ice . - αλεχολυτ 5:03 pm
 std::string s = "abc"; // s.reserve(100); char& c = s[0]; for(;s[0] == c;) { s += "def"; ++c; cout << s << endl; } 

This code (and the one with the reserve line) clearly shows that in the first case troubles come much earlier.

But, depending on what kind of volume is allocated to the line initially - maybe the other way around.

But, as it seems to me (there is no standard at hand), the third code is still correct, while the second one is not.