std::vector<std::wstring> Is moving/reallocating inner wstring.data() legal?

夙愿已清 提交于 2021-01-27 13:31:40

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!