问题
Here is an excerpt:
...
std::vector<std::wstring> vecWstr;
vecWstr.emplace_back(L"1");
wchar_t* data1 = vecWstr[0].data(); //<-This pointer needed for future use.
vecWstr.emplace_back(L"2");
wchar_t* data2 = vecWstr[0].data();
if (data1 != data2)
MessageBox(L"Error, not equal.", L"Compare");
MessageBox
always arises.
So, here i compare two wstring
buffers before and after.emplace()
. In my understanding they must be equal.
The main concern here is: Why vector
moves/reallocates 1st innner std::wstring
element after emplacing a second?
This question arose after investigation took place after weird program behavior.
If i save vecWstr[0].data()
buffer pointer before second .emplace()
then buffer pointer becomes obsolete and program behaves inappropriately.
The biggest issue is that there are a lot of std::vector<std::wstring>
's in a program, but all of them seem to work as expected, and only one so far like showed above.
This is all in MSVS 16.1.5
The question is:
Who is right here? Can std::vector
change/move the inner buffer of its std::wstring
elements or not?
回答1:
In C++ STL, there is something called pointer invalidation. This means, when you obtain a pointer of an element in a container, and later you modified the container, after the modification your pointer may be no longer valid.
The rule of pointer invalidation is defined by the standard and varies between containers to containers, operations to operations.
In your case, you have a std::vector
. A reference/pointer/iterator to a vector element is no longer valid if you emplace_back
and vector needs bigger capacity for the added element. In this case, the vector allocate another bigger space in memory and moves all its elements there.
But wait!
You are taking data()
pointer directly from the string! Why is this pointer also invalidated? Shouldn't wstring
be a light-weight struct that just contains pointer to some heap buffer?
Well, this is the magic of SSO (Small String Optimization). If your string is small enough, wstring
just stores its buffer in the data structure itself (rather than storing a pointer to a buffer) . In this case, when you move it around, of course the pointer is invalidated.
Your string is pretty small (1 wide char), so it satisfies the condition of SSO. If you use longer ones:
std::vector<std::wstring> vecWstr;
vecWstr.emplace_back(L"asdfghjkl");
wchar_t* data1 = vecWstr[0].data(); //<-This pointer needed for future use.
vecWstr.emplace_back(L"qwertyuiop");
wchar_t* data2 = vecWstr[0].data();
if (data1 != data2)
MessageBox(0, L"Error, not equal.", L"Compare", 0);
return 0;
The messagebox would probably not pop.
However you don't have control over the length of the run-time string, and you don't know how your compiler would implement SSO, so don't code this way!
Instead, you can use the reserve
method (as songyuanyao suggested), or use other containers that don't invalidate pointers when adding element. Please refer to std::list and std::deque. Read the sections about their pointer/reference/iterator invalidation.
来源:https://stackoverflow.com/questions/56896539/stdvectorstdwstring-is-moving-reallocating-inner-wstring-data-legal