C++:从vector<vector<T>>获取T**

Posted

技术标签:

【中文标题】C++:从vector<vector<T>>获取T**【英文标题】:C++: Getting T** from vector<vector<T>> 【发布时间】:2017-10-24 01:15:04 【问题描述】:

我正在与需要float** 类型的二维数组的 C 库交互,其大小在运行时确定。我想使用诸如 std::vector 之类的 STL 容器来管理此内存,但 vector&lt;vector&lt;float&gt;&gt;::data() 提供 vector&lt;float&gt;*,而不是 float**。我能做什么?

【问题讨论】:

分配您自己的float**vector&lt;float*&gt; 并用指向内部向量data() 的指针填充它。您不能免费获得float** - 它只是不存在于您选择的数据结构中。 请问为什么我的问题被否决了?我非常努力地简洁地提出这个问题,并在发布之前进行了广泛的搜索。我怎么能做得更好?公认的答案很有趣且有见地。不明白为什么这篇文章不受欢迎...... 【参考方案1】:

您可以创建一个新的 vector,其中包含指向由您的 vector of vectors 的内部 vector 管理的所有内部数组的指针:

void old_c_function(float** floats, std::size_t X, std::size_t Y)

    for(auto x = 0U; x < X; ++x)
        for(auto y = 0U; y < Y; ++y)
            std::cout << "[" << x << ", " << y << "] = " << floats[x][y] << '\n';


int main()

    std::vector<std::vector<float>> v =
    
        1.2, 3.4, 5.6,
        7.8, 9.0, 1.2,
    ;

    // create a new vector to hold the pointers to the arrays
    // managed by the internal vectors
    std::vector<float*> v_ptrs;
    v_ptrs.reserve(v.size());

    // add the addresses of all the arrays to the new vector
    std::for_each(std::begin(v), std::end(v),
        [&v_ptrs](auto& v) v_ptrs.push_back(v.data()); );

    // call your legacy function using your pointer vector
    old_c_function(v_ptrs.data(), v.size(), v.front().size());

输出:

[0, 0] = 1.2
[0, 1] = 3.4
[0, 2] = 5.6
[1, 0] = 7.8
[1, 1] = 9
[1, 2] = 1.2

注意:

显然,如果您更改向量,则需要重建指针向量,因为地址可能会发生变化。

您可以通过一些包装函数即时重建它,如下所示:

void new_wrapper_function(std::vector<std::vector<float>>& v)

    // create a new vector to hold the pointers to the arrays
    // managed by the internal vectors
    std::vector<float*> v_ptrs;
    v_ptrs.reserve(v.size());

    // add the addresses of all the arrays to the new vector
    std::for_each(std::begin(v), std::end(v)
        [&v_ptrs](auto& v) v_ptrs.push_back(v.data()); );

    // call your legacy function using your pointer vector
    old_c_function(v_ptrs.data(), v.size(), v.front().size());

或者(我最喜欢的)构建一个包装器 class 来封装两个向量,并在主向量在其一个维度上增加容量时更新指针向量。

【讨论】:

... 然后您希望 C 函数只是有一个草率的声明,实际上将其参数视为 float* const* @aschepler C 函数可以毫无问题地更新数组,指针指向原始数组中的实际数据。 std::copystd::back_inserter(v) 怎么样? @aschepler 因为我没有复制float 值,所以我正在获取每个内部数组的第一个元素的地址。 哦,呵呵。 std::vector&lt;float&gt; 不会很好地初始化 float*。没有。【参考方案2】:

我会说使用

typedef std::pair<int, int> shape;
std::pair<float **, shape> obj;

std::tuple<float **, int, int> obj;

随你喜欢。

【讨论】:

【参考方案3】:

我能做什么?

a) 使用一维向量

 typedef std::vector<float> FloatVec_t

b) 要访问一维向量,您可以从二维坐标计算一维索引,这是一个可以优化掉的简单函数。

 // size_t maxCol; is runtime size info

 size_t indx1D(size_t r, size_t c) 
  return static_cast<size_t>((r*maxCol) + c); 

c) 创建/使用 getElement(x,y) 进行二维访问:

 FloatVec_t  fltVec1D;  

 // 2d access
 float* getElement(size_t r, size_t c)  return (fltVec [indx1D (r,c)]); 

d) 在一维向量之上创建 arr。请参阅下面的工作代码。

环境:

Ubuntu 15.10; g++ 版本 5.2.1 20151028;旧版戴尔速龙

代码/MCVE:

#include "../../bag/src/dtb_chrono.hh"
#include <iostream>
#include <iomanip>
#include <vector>
#include <cassert>

typedef std::vector<float> FloatVec_t;

class T537_t

private:
   FloatVec_t fltVec1D;
   size_t     maxCol;
   size_t     maxRow;

public:
   T537_t () = default;
   ~T537_t() = default;

   int exec(size_t a_MaxCol,
            size_t a_MaxRow)
      
         maxCol = a_MaxCol;
         maxRow = a_MaxRow;

         size_t N = maxCol * maxRow;

         std::cout << "\n  sizeof(float): " << sizeof(float) << std::endl;

         std::cout << "\n  Col x Row: " << maxCol << " x " << maxRow
                   << " = " << N

                   << "\n\n  reserved " << N << " elements in 1D vec" << std::endl;

         fltVec1D.reserve(N);

         std::cout << "\n  initialize 1D vec with values computed from row and col" << std::flush;

         for (uint r=1; r<=maxRow; ++r)
            for (uint c=1; c<=maxCol; ++c)
               fltVec1D.push_back (100.1F * static_cast<float> (((r-1)*maxCol) + c));

         std::cout << "\n\n  report fltVec1D addrs: " << std::flush;
         for (uint r=0; r<maxRow; ++r)
         
            std::cout << "\n    fltVec1D[" << std::setw(2) << r << "]  " << std::flush;
            for (uint c=0; c<maxCol; ++c)
               std::cout << "  " << std::setw(8)
                         << &fltVec1D[indx1D(r,c)] << std::flush;
            std::cout << std::flush;
         

         std::cout << "\n\n  report fltVec1D data: " << std::flush;
         for (uint r=0; r<maxRow; ++r)
         
            std::cout << "\n    fltVec1D[" << std::setw(2) << r << "]  " << std::flush;
            for (uint c=0; c<maxCol; ++c)
               std::cout << "  " << std::setw(8) << std::setprecision(5)
                         << fltVec1D[indx1D(r,c)] << std::flush;
            std::cout << std::flush;
         

         // overlay arr on top of fltVec1D data
         float** arr   = new float* [maxRow]; // arr contains rows
         
            for (size_t r=0; r < maxRow; ++r)
               arr[r] = getElement(r,0);      // rows already exist
                                             // and start at col 0,


         std::cout << "\n\n  report arr data: " << std::flush;
         for (size_t r=0; r<maxRow; ++r)
         
            std::cout << "\n         arr[" << std::setw(2) << r << "]  " << std::flush;
            for (size_t c=0; c<maxCol; ++c)
            
               std::cout << "  " << std::setw(8) << std::setprecision(5)
                         << arr[r][c] << std::flush;
            
            std::cout << std::flush;
         

         std::cout << "\n\n\n  report address diffs:   &arr[r][c] - getElement(r,c) : \n" << std::flush;
         for (uint r=0; r<maxRow; ++r)
         
            std::cout << "\n          row" << std::setw(2) << r << "   " << std::flush;
            for (uint c=0; c<maxCol; ++c)
            
               float* addr1 = & arr[r][c];
               float* addr2 = getElement(r,c);
               std::cout << "  " << std::setw(8) << (addr1 - addr2) << std::flush;
            
            std::cout << std::flush;
         

         delete[] arr; // delete of  float** arr   = new float* [maxCol];

         return 0;
      

private: // methods

   size_t indx1D(size_t r, size_t c)
      
         assert(r<maxRow);
         assert(c<maxCol);
         return static_cast<size_t>((r*maxCol) + c);
      

   float* getElement(size_t r, size_t c)  return (& fltVec1D [indx1D(r,c) ] ); 

; // class T537_t


int main(int argc, char* argv[])

   if(argc > 1)
   
      std::cout << "\nargc: " << argc << std::endl;
      for (int i = 0; i < argc; i += 1) std::cout << argv[i] << " ";
      std::cout << std::endl;
   

   if(3 != argc) 
      std::cerr << "\n  2 Required parameters: maxCol maxRow " << std::endl;
      return (0);
   

   setlocale(LC_ALL, "");
   std::ios::sync_with_stdio(false);

   int retVal = -1;
   
      T537_t   t537;

      Time_t start_us = HRClk_t::now();

      retVal = t537.exec(static_cast<size_t>(std::atoi(argv[1])),   // col - i.e. 5
                         static_cast<size_t>(std::atoi(argv[2])));  // row - i.e. 20

      auto  duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);

      std::cout << "\n\n  t537.exec() duration  " << duration_us.count() << " us" << std::endl;
   

   return(retVal);

命令行参数的输出示例 3 5

  sizeof(float): 4

  Col x Row: 3 x 5 = 15

  reserved 15 elements in 1D vec

  initialize 1D vec with values computed from row and col

  report fltVec1D addrs: 
    fltVec1D[ 0]    0x179dc50  0x179dc54  0x179dc58
    fltVec1D[ 1]    0x179dc5c  0x179dc60  0x179dc64
    fltVec1D[ 2]    0x179dc68  0x179dc6c  0x179dc70
    fltVec1D[ 3]    0x179dc74  0x179dc78  0x179dc7c
    fltVec1D[ 4]    0x179dc80  0x179dc84  0x179dc88

  report fltVec1D data: 
    fltVec1D[ 0]       100.1     200.2     300.3
    fltVec1D[ 1]       400.4     500.5     600.6
    fltVec1D[ 2]       700.7     800.8     900.9
    fltVec1D[ 3]        1001    1101.1    1201.2
    fltVec1D[ 4]      1301.3    1401.4    1501.5

  report arr data: 
         arr[ 0]       100.1     200.2     300.3
         arr[ 1]       400.4     500.5     600.6
         arr[ 2]       700.7     800.8     900.9
         arr[ 3]        1001    1101.1    1201.2
         arr[ 4]      1301.3    1401.4    1501.5


  report address diffs:   &arr[r][c] - getElement(r,c) : 

          row 0            0         0         0
          row 1            0         0         0
          row 2            0         0         0
          row 3            0         0         0
          row 4            0         0         0

总结:

本单元测试代码演示:

a) 构建并初始化一维 std::vector。

b) 使用内置数组构造和 new 创建“float** arr”。

c) 快速 for 循环使用 getElement(r,0) 加载每个行指针。

d) 单元测试显示来自 1d vec (fltVec1D) 和 arr 的相同信息(地址和数据)

e) 测试通过比较地址来结束,这表明这两个名称提供了对相同地址和相同数据的访问。

“arr”适用于交付给遗留功能(未测试)。

【讨论】:

这如何帮助将某些内容传递给需要 float** 的现有函数? 通常当函数文档声称 float** p 表示一个 mxn 二维数组时,这意味着元素是 p[i][j] 其中 i&lt;m &amp;&amp; j&lt;n,而不是 *p[i*n+j]。 (是的,所以这里不涉及数组数组。不过。) @aschepler - 感谢您的问题和评论.. 我决定提供一些代码,并且不得不寻找它,然后修改它。

以上是关于C++:从vector<vector<T>>获取T**的主要内容,如果未能解决你的问题,请参考以下文章

从 Swift 调用 C++ - std::vector<T> 的等价物

C++ - 迭代从 find_if 返回的 std::vector<>

C++从vector中删除指定元素

将 std::vector<double> 从 C++ 包装到 C 以在 Swift 中使用

C++ vector容器用法

c++:从vector::insert返回迭代器