繁体   English   中英

将向量的向量复制到一维数组中

[英]Copy vector of vectors into 1D array

我有以下 C++ 对象

std::vector<std::vector<SomeClass>> someClassVectors(sizeOFOuter);

我知道“外部”向量的大小,但“内部”向量的大小各不相同。 我需要将此结构的元素复制到一维数组中,如下所示:

SomeClass * someClassArray;

我有一个解决方案,我像这样使用 std::copy

int count = 0;
for (int i = 0; i < sizeOfOuter; i++)
{
   std::copy(someClassVectors[i].begin(), someClassVectors[i].end(), &someClassArray[count]);
   count += someClassVectors[i].size();
}

但该类包含大矩阵,这意味着我不能同时分配两次“向量”结构和一维数组。

有任何想法吗?

您之前是否将someClassArray预先分配给给定的大小? 如果可能的话,我建议使用一维向量来摆脱普通数组的已知问题。

这样的事情怎么样:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {

     std::vector<std::vector<int>>  someClassVectors {
        {1,2,3},
        {4,5,6},
        {7,8,9}
     };

    std::vector<int> flat;  
    while (!someClassVectors.empty())
    {
       auto& last = someClassVectors.back();
    
       std::move(std::rbegin(last), std::rend(last), std::back_inserter(flat));
    
       someClassVectors.pop_back();
    }
    
    std::reverse(std::begin(flat), std::end(flat));
    
    int * someClassArray = flat.data();

    std::copy(someClassArray, someClassArray + flat.size(), std::ostream_iterator<int>(std::cout, " "));

}

额外的反向操作对内存指标没有影响 - 这种方法有助于避免因从头到尾删除向量元素而导致的不必要的内存重新分配。

编辑受评论的启发,我更改了副本以移动语义

拥抱Range-v3 (或将在 C++20 中引入的任何内容)并在(几乎)一行中编写解决方案:

auto flattenedRange = ranges::views::join(someClassVectors);

这为您提供了flattenedRange的范围,您可以轻松地循环或复制到其他地方。

这是一个可能的用例:

#include <iostream>
#include <vector>

#include <range/v3/view/join.hpp>

int main()
{
    std::vector<std::vector<int>> Ints2D = {
        {1,2,3},
        {4},
        {5,6}
    };

    auto Ints1D = ranges::views::join(Ints2D);
    // here, going from Ints1D to a C-style array is easy, and shown in the other answer already

    for (auto const& Int : Ints1D) {
        std::cout << Int << ' ';
    }
    std::cout << '\n';
    // output is: 1 2 3 4 5 6 
}

如果您想获得真正的std::vector而不是范围,在将其写入 C 样式数组之前,您可以包含其他标头

#include <range/v3/range/conversion.hpp>

并将 pipe join的输出转换为转换函数:

    auto Ints1D = ranges::views::join(Ints2D) | ranges::to_vector;
    // auto deduces std::vector<int>

在标准和版本方面,它并不真正需要太多。 这个演示中,您可以看到它编译并运行得很好

  • 编译器 GCC 7.3
  • 库 Range-v3 0.9.1
  • C++14 标准(选项-std=c++14g++

至于副本

  • ranges::views::join(Ints2D)仅在Ints2D上创建视图,因此不会发生复制; 如果视图对您没有意义,您可能需要查看C++ 中的函数式编程中的第 7 章,其中对范围、图片和所有内容进行了非常清晰的解释;¹
  • 甚至将该输出分配给一个变量, auto Ints1D = ranges::views::join(Ints2D); 不会触发副本; Ints1D在这种情况下不是std::vector<int> ,即使当我们循环它时它表现得像一个(表现得像一个向量,因为它是它的一个视图);
  • 将其转换为向量,例如通过| ranges::to_vector | ranges::to_vector ,显然会触发一个副本,因为您不再请求对向量的视图,而是一个真实的视图;
  • 将范围传递给在其元素上循环的算法不会触发复制。

这是您可以尝试的示例代码:

// STL
#include <iostream>
#include <vector>
// Boost and Range-v3
#include <boost/range/algorithm/for_each.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/range/conversion.hpp>

struct A {
    A() = default;
    A(A const&) { std::cout << "copy ctor\n"; };
};

int main()
{
    std::vector<std::vector<A>> Ints2D = {
        {A{},A{}},
        {A{},A{}}
    };
    using boost::range::for_each;
    using ranges::to_vector;
    using ranges::views::join;
    std::cout << "no copy, because you're happy with the range\n";
    auto Ints1Dview = join(Ints2D);
    std::cout << "copy, because you want a true vector\n";
    auto Ints1D = join(Ints2D) | to_vector;
    std::cout << "copy, despite the refernce, because you need a true vector\n";
    auto const& Ints1Dref = join(Ints2D) | to_vector;
    std::cout << "no copy, because we movedd\n";
    auto const& Ints1Dref_ = join(std::move(Ints2D)) | to_vector;
    std::cout << "no copy\n";
    for_each(join(Ints2D), [](auto const&){ std::cout << "hello\n"; });
}

¹ 为了尝试给出范围是什么的线索,我想说的是,您可以将它想象成一个包装两个迭代器的东西,一个指向范围的end ,另一个指向范围的begin ,后者可通过operator++递增; 这个操作符会以正确的方式处理跳转,例如,在查看Ints2D的元素3 (在Ints2D[0][2] )后, operator++将使迭代器跳转到查看元素Ints[1][0] .

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM