嵌套 for_each 循环导致向量大小意外增加

Nested for_each loops causing unexpected increase in size of a vector

我有以下代码用于将二维双精度数组转换为一维向量。使用 std::vector 分配数组,并使用嵌套的 for_each 循环将二维数组的内容传输到一维数组。

#include <iostream>
#include <algorithm>
#include <vector>
#include <stdexcept>
#define UNUSED(expr) (void)(expr)

using usll = __uint64_t;

void print1d(std::vector<double> vv);
void matToVect1d(const std::vector<std::vector<double>>& v2d, std::vector<double>& v1d);
void matToVect1dEx(const std::vector<std::vector<double>>& v2d, std::vector<double>& v1d);

int main(int argc, char* argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    std::cout << std::endl;

    const usll DIM0 {10};
    const usll DIM1 {8};

    std::vector<std::vector<double>> data2d(DIM0, std::vector<double>(DIM1));
    std::vector<double> data1d(DIM0 * DIM1);
    double temp = 0.0;

    for (usll i{}; i<DIM0; ++i)
    {
        for (usll j{}; j<DIM1; ++j)
        {
            data2d[i][j] = temp++;
        }
    }

    try
    {
        matToVect1d(data2d, data1d);
        std::cout << "2D array data2d as a 1D vector is:" << std::endl;
        print1d(data1d);
        std::cout << std::endl;
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    std::cout << "Press enter to continue";
    std::cin.get();

    return 0;
}

void print1d(std::vector<double> vv)
{
    for (size_t i{}; i<vv.size(); ++i)
    {
        std::cout << vv[i] << "  ";

        if ((i+1)%10 == 0)
        {
            std::cout << std::endl;
        }
    }

    std::cout << std::endl;
}

void matToVect1d(const std::vector<std::vector<double>>& v2d, std::vector<double>& v1d)
{
    if (v1d.size() != v2d.size()*v2d[0].size())
    {
        throw std::runtime_error("An exception was caught. The sizes of the input arrays must match.");
    }

    for_each(v2d.cbegin(), v2d.cend(), [&v1d](const std::vector<double> vec)
    {
        for_each(vec.cbegin(), vec.cend(), [&v1d](const double& dValue)
        {
            v1d.emplace_back(dValue);
        });
    });

}

void matToVect1dEx(const std::vector<std::vector<double>>& v2d, std::vector<double>& v1d)
{
    usll index{};

    if (v1d.size() != v2d.size()*v2d[0].size())
    {
        throw std::runtime_error("An exception was caught. The sizes of the input arrays must match.");
    }

    for (usll i=0; i<v2d.size(); ++i)
    {
        for (usll j=0; j<v2d[0].size(); ++j)
        {
            index = j + i*v2d[0].size();
            v1d[index] = v2d[i][j];
        }
    }
}

每次我运行代码,输出是:

0  0  0  0  0  0  0  0  0  0 
0  0  0  0  0  0  0  0  0  0 
0  0  0  0  0  0  0  0  0  0 
0  0  0  0  0  0  0  0  0  0 
0  0  0  0  0  0  0  0  0  0 
0  0  0  0  0  0  0  0  0  0 
0  0  0  0  0  0  0  0  0  0 
0  0  0  0  0  0  0  0  0  0 
0  1  2  3  4  5  6  7  8  9 
10  11  12  13  14  15  16  17  18  19 
20  21  22  23  24  25  26  27  28  29 
30  31  32  33  34  35  36  37  38  39 
40  41  42  43  44  45  46  47  48  49 
50  51  52  53  54  55  56  57  58  59 
60  61  62  63  64  65  66  67  68  69 
70  71  72  73  74  75  76  77  78  79

这是原始一维数组的两倍。什么?零是从哪里来的?是什么导致向量大小从 80 增加到 160?相反,当我将 for_each 循环更改为常规 for 循环时,我得到了正确的输出:

0  1  2  3  4  5  6  7  8  9 
10  11  12  13  14  15  16  17  18  19 
20  21  22  23  24  25  26  27  28  29 
30  31  32  33  34  35  36  37  38  39 
40  41  42  43  44  45  46  47  48  49 
50  51  52  53  54  55  56  57  58  59 
60  61  62  63  64  65  66  67  68  69 
70  71  72  73  74  75  76  77  78  79 

我怀疑异常是由于使用了 for_each 算法,然而 Meyer (2018) 在他的书中 Effective C++ Digital Collection: 140 Ways to Improve Your Programming says "the algorithm call is preferable." He says, and I quote: “从效率的角度来看,算法可以通过三种方式击败显式循环,两种主要方式,一种次要方式。次要方式涉及消除 冗余计算。

在我的实际用例中,方法 matToVect1d() 被调用了几次 1000 并且每个数组的元素总数为 240 x 200

我的最后一个问题是使用for_each算法实现循环是否有意义?答案将不胜感激。

std::vector<double> data1d(DIM0 * DIM1); <- 这行代码没有 "reserve"。它用 DIM0 * DIM1 默认初始化双精度值填充向量。 这是您正在使用的构造函数。 vector( size_type count, const T& value, const Allocator& alloc = Allocator());

检查此以获取更多参考: https://en.cppreference.com/w/cpp/container/vector/vector

改成这样:

std::vector<double> data1d();
data1d.reserve(DIM0 * DIM1);

是因为线

std::vector<double> data1d(DIM0 * DIM1);        

其中 data1d 创建一个 向量 doubles 并用 0.0 s in DIM0 x 初始化它DIM0 大小。 因此,稍后在 std::for_each 的情况下,当您 std::vector::emplace_back 时,您将在其后插入元素。因此,您会看到 DIM0 x DIM0 0 s,然后是您插入的元素。

您需要 std::vector::reserve 来实现所需的行为

std::vector<double> data1d;
data1d.reserve(DIM0 * DIM1);  // for unwated reallocations

I suspect the anomaly is due to the use of the for_each algorithm[...]

,原因(即错误)如前所述


My final question is whether it makes sense to implement the loop using the std::for_each algorithm?

std::for_each 方法没有任何问题,除了您在第一个 lambdas 参数列表中复制 std::vector<double> 并且(可能)不太可读。

std::for_each(v2d.cbegin(), v2d.cend(), [&v1d](std::vector<double> const& vec)
                                               //                  ^^^^^^ --> prefer const-ref
{
    for_each(vec.cbegin(), vec.cend(), [&v1d](const double dValue)
    {
        v1d.emplace_back(dValue);
    });
});

比下面显示的 range - for-loop 方法

std::vector<double> data1d;
data1d.reserve(DIM0 * DIM1);  // for unwated reallocations

// ... later in the function

for (const std::vector<double>& vector : v2d)
    for (const double element : vector)
        v1d.emplace_back(element);