Why does this use of std::transform cause exc_bad_access

好久不见. 提交于 2021-01-28 00:57:54

问题


I have this code (roughly) in a cpp app:

QList<Foo> rawAssets = getlist();
QVector<std::pair<QString, QString>> assets;
std::transform(rawAssets.begin(), rawAssets.end(), assets.begin(), makePair);

That causes exc_bad_access to throw when I use assets again.

However, if I change assets.begin() to std::back_inserter(assets) then it works as I expect.

I found tutorials for std::transform showing both kinds of usage. Why is it wrong in my case?


回答1:


Since you've just declared QVector<std::pair<QString, QString>> assets; it's empty. assets.begin() is therefore equal to assets.end(), and points past-the-end of the empty vector.

The third parameter of std::transform is an iterator through which transform will write the results of the transformation (and increment it after each write).

When you pass in assets.begin(), transform will write through this past-the-end iterator, resulting in an out-of-bounds write. It's roughly the same as doing char x[3]; x[4] = 'a';

When you pass in std::back_inserter(assets), you create a special iterator such that writing through it actually inserts the written element into assets. So all is well.

The first form could be used if assets was already of sufficient size, and you wanted to overwrite the elements in it. The second form is used when you want to extend assets with the results of the transformation.




回答2:


assets starts out as an empty vector.

transform()'s third parameter is an output iterator. Your code is essentially equivalent to the following:

QVector<std::pair<QString, QString>> assets;

auto output_iter=assets.begin();

// You now call transform(), passing output_iter
//
// transform() then essentially does the following:

*output_iter++ = /* first transform()ed value */;
*output_iter++ = /* second transform()ed value */;

// ... and so on.

That's how transform() works. Since assets() is an empty vector, begin() gives you the ending iterator value, and then the code proceeds to merrily write past the end of the vector, and crap all over itself.

You have two basic options:

  1. Before obtaining the begin() iterator, resize() the vector to the number of elements you're about to transform(), so transform() ends up filling out a pre-resized array's contents, exactly. However, since your data comes from a list, this is not easily available, so:

  2. Pass a std::back_insert_iterator to transform(), instead of an iterator to the empty array.




回答3:


std::transform() assumes that it can write directly into the output. Which in your case is a vector of zero size. You can fix the bug by explicitly resizing your vector to the size of the transform input, or by using back_inserter as you already discovered.




回答4:


If you resize assets to the same size as rawAssets before the transform, it won't cause a bad access. Using a back_insert_iterator causes push_back to be called for each iterator of transform. Without that, it tries to access the first, second, third, etc element of assets, which is still empty, therefore causing a bad access.



来源:https://stackoverflow.com/questions/42887034/why-does-this-use-of-stdtransform-cause-exc-bad-access

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