为什么使用std :: transform会导致exc_bad_access

时间:2017-03-19 13:17:40

标签: c++ qt stl exc-bad-access

我在cpp app中大致有这段代码:

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

当我再次使用exc_bad_access时,会导致assets投掷。

但是,如果我将assets.begin()更改为std::back_inserter(assets),那么它会按照我的预期运作。

我找到了std::transform的教程,展示了两种用法。为什么我的情况不对?

4 个答案:

答案 0 :(得分:3)

因为您刚刚宣布QVector<std::pair<QString, QString>> assets;它的为空。 assets.begin()因此等于assets.end(),并指向空矢量的末尾。

std::transform的第三个参数是一个迭代器,transform转换结果(并在每次写入后递增)。

当你传入assets.begin()时,transform将通过这个过去的迭代器进行写入,从而导致越界写入。它与执行char x[3]; x[4] = 'a';

大致相同

当您传入std::back_inserter(assets)时,您创建了一个特殊的迭代器,以便通过它实际写入写入的元素插入assets。一切都很好。

如果assets已经足够大,并且您希望覆盖其中的元素,则可以使用第一个表单。如果您希望扩展 assets并使用转换结果,则使用第二种形式。

答案 1 :(得分:1)

assets以空矢量开头。

transform()的第三个参数是输出迭代器。您的代码基本上等同于以下内容:

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.

这就是transform()的工作方式。由于assets()是一个空向量,begin()为你提供了结束迭代器值,然后代码继续快速地写过向量的末尾,然后把它自己废弃。

您有两个基本选项:

  1. 在获取begin()迭代器之前,resize()向量为transform()元素的数量,因此transform()最终会填写一个完全预先调整数组的内容。但是,由于您的数据来自列表,因此不容易获得,因此:

  2. std::back_insert_iterator传递给transform(),而不是将迭代器传递给空数组。

答案 2 :(得分:0)

std::transform()假设它可以直接写入输出。在您的情况下,这是一个零大小的向量。您可以通过将矢量显式调整为变换输入的大小,或者使用已发现的back_inserter来修复错误。

答案 3 :(得分:0)

如果您在assets之前将rawAssets调整为与transform相同的尺寸,则不会导致访问权限错误。使用back_insert_iterator导致为push_back的每个迭代器调用transform。没有它,它会尝试访问assets的第一个,第二个,第三个等元素,它仍然是空的,因此导致访问不良。