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

unb*_*d37 3 c++ arrays algorithm stdvector c++17

我有以下一段代码,用于将二维数组转换为一维向量。数组使用 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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

每次运行代码时,输​​出都是:

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
Run Code Online (Sandbox Code Playgroud)

这是原始一维数组的两倍。什么?零从哪里来?是什么导致向量大小从 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 
Run Code Online (Sandbox Code Playgroud)

我怀疑异常是由于使用了 for_each 算法,但 Meyer (2018) 在他的书Effective C++ Digital Collection: 140 Ways to Improvement Your Programming 中说“算法调用更可取”。他说,我引用:“从效率的角度来看,算法可以通过三种方式击败显式循环,两种主要方式,一种次要方式。次要方式涉及消除冗余计算。”

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

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

JeJ*_*eJo 7

这是因为线

std::vector<double> data1d(DIM0 * DIM1);        
Run Code Online (Sandbox Code Playgroud)

在那里data1d你创建一个s向量double并用0.0s 在DIM0xDIM0的大小初始化它。因此,以后std::for_each以防万一,当您std::vector::emplace_back在此之后插入元素。因此,您会看到DIM0xDIM0 0和您插入的元素。

您需要std::vector::reserve的是所需的行为

std::vector<double> data1d;
data1d.reserve(DIM0 * DIM1);  // for unwated reallocations
Run Code Online (Sandbox Code Playgroud)

我怀疑异常是由于使用了 for_each 算法[...]

,由于上述原因(即错误)。


我的最后一个问题是使用std::for_each算法实现循环是否有意义?

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

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);
    });
});
Run Code Online (Sandbox Code Playgroud)

范围 -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);
Run Code Online (Sandbox Code Playgroud)


Mar*_*zek 5

std::vector<double> data1d(DIM0 * DIM1);<- 这行代码不“保留”。它用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);
Run Code Online (Sandbox Code Playgroud)