在2d C数组上使用std :: transform转换为1d C数组

时间:2018-06-25 18:27:24

标签: c++ multidimensional-array stl stl-algorithm

我正在尝试使用std::transform将一个简单的2d数组(Foo个结构)转换为一个1d(转换后的)Bar结构的数组。

// convert 2d array of Foo structs to 1d array of Bar structs
// suitable for constructing a Baz struct.
Foo array2d[3][2] = 
{ 
    { 
        {1,2}, {3,4} 
    }, 
    { 
        {5,6}, {7,8} 
    }, 
    { 
        {9,10}, {11,12} 
    } 
};

在此简单示例中,转换只是颠倒了字段顺序,因为两个结构实际上是同一类型。在我的应用程序中,这些是完全不同的类型。

using Foo = struct {
    uint32_t a;
    uint32_t b;
};

using Bar = struct {
    uint32_t c;
    uint32_t d;
};

这个想法是,可以使用此Bar结构的1d数组构造一个Baz结构。

我在使用lambda转换器时遇到了麻烦。我相信,外层的时间在实际Foo-> Bar转换发生时一次排成一行,而内层的时间在一列进行一次换行。在现场演示中,我必须注释掉2d数组的std :: transform,并用一个扁平版本替换它,在该版本中,我将2d数组转换为1d数组(大小为row times col)。这项工作完美无瑕-但是我试图坚持使用参数类型,而不必求助于reinterpret_cast <>。

    std::vector<Bar> array1d;
    array1d.reserve(Baz::gArraySize);
#if 0
    // I don't know how to use the transform on the 2d array
    std::transform(std::cbegin(array2d), std::cend(array2d),
        std::back_inserter(array1d),
        [](const Foo(&rRow)[Baz::gNumCols]) {
        std::transform(std::cbegin(rRow), std::cend(rRow),
            [](const Foo& rNext) -> Bar {
                // reverse the order of the fields
                return Bar{ rNext.b, rNext.a };
            });
        });
#else
    // Only workaround is to cast the 2d array to a 1d array using reinterpret cast<>
    const auto& special = reinterpret_cast<const Foo(&)[Baz::gArraySize]>(array2d);
    // I don't know how to use the transform on the 2d array
    std::transform(std::cbegin(special), std::cend(special),
        std::back_inserter(array1d),
        [](const Foo& rNext) -> Bar {
            // reverse the order of the fields
            return Bar{ rNext.b, rNext.a };
        });
#endif
    // construct from transformed 2d array
    Baz myBaz(reinterpret_cast<const Bar(&)[Baz::gArraySize]>(array1d[0]));
    std::cout << myBaz;

产生预期的输出,如下所示:

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Bar{c=2, d=1},
Bar{c=4, d=3},
Bar{c=6, d=5},
Bar{c=8, d=7},
Bar{c=10, d=9},
Bar{c=12, d=11},

结构像C数组形式,因为它们来自外部。我不确定std :: transform是否可以实现目标,但是我想使用STL算法,而不是手工解开循环。

我创建了以下live coliru demo来展示我要实现的目标-但是在进行适当的转换时会出现很多错误。请注意,传递给Baz的数组取决于以下事实:std :: vector在内存中连续分配数据结构(这由STL保证)。

struct Baz {
    constexpr static int gNumRows = 3;
    constexpr static int gNumCols = 2;
    constexpr static int gArraySize = gNumRows * gNumCols;
    Bar arrayField[gArraySize];
    // explicit constructor from C style fixed size array.
    explicit Baz(const Bar(&rParam)[gArraySize])
        : arrayField{}
    {
        std::memcpy(arrayField, rParam, gArraySize * sizeof(Bar));
    }

    friend std::ostream& operator<<(
        std::ostream& os, const Baz& rhs) {
        for (auto next : rhs.arrayField) {
            os << "Bar{c=" << next.c << ", d=" << next.d << "},\n";
        }
        return os;
    }

};

1 个答案:

答案 0 :(得分:1)

您传递给外部transform的lambda不会返回任何内容,它实际上也不会返回任何值,因为它应该为输入范围的每个元素(您的两个元素)返回一个值维数组)。但是该数组的每个元素都有两个值,因此transform的每次迭代在产生一个值时都会产生两个值,这就是为什么您不能在这里使用transform的原因。

鉴于此,在这里使用简单的循环会容易得多,并且可读性强:

for (auto &&row : array2d)
    for (auto &&foo : row)
        oneDimArray.push_back(Bar{ foo.b, foo.a });

在实际使您的生活更轻松的情况下保留STL算法:)。