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

Why does this use of std::transform cause exc_bad_access

我在 cpp 应用程序中(大致)有这段代码:

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 的教程显示了这两种用法。为什么我的情况不对?

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

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

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()结束up 准确地填充预先调整大小的数组的内容。但是,由于您的数据来自列表,因此不容易获得,因此:

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

由于您刚刚声明 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 时使用第二种形式。