【问题标题】:C++: std::vector - It is possible to "slice" a vector?C++:std::vector - 可以“切片”向量吗?
【发布时间】:2015-08-23 10:32:18
【问题描述】:

我正在编写一些代码来集成 ODE。这个问题既是对编码建议的请求,也是对解决方案的请求,所以如果您对我即将提供的建议有其他建议,请告诉我!

要由 ODE 积分器积分的“对象”以 6 个“块”的形式出现...原因是我有一个双精度的 std::vector,它们按以下方式排列:

前 3 个双精度值是位置坐标; x、y 和 z。 接下来的 3 个双精度是速度坐标; x、y 和 z。

所以,现在你知道了,我有一个函数,它接受成对的“位置”“”“向量”“” 作为参数并返回某种结果......看看我在哪里我要这样做吗?

目前该函数需要 2 批位置坐标,方式如下:

std::vector<double> magic_gravity_formula(const std::vector<double> &r1,
          const std::vector<double> &r2, const double m1, const double m2)

我不想将 3 个一组的所有数据复制到新向量中 - 这是一种疯狂(而且非常慢)的编程方式。

可以改用指向原始数据的指针...并且只需将指针传递给 x 坐标(3 个双精度块中的第一项) - 这对我来说似乎还可以,但是也许有更好的方法?有点像 Python 或 Matlab 数组切片?我可以这样做吗?

我有点想传递一个新的向量(或某种包装类?),从已经存储在数组中的数据创建...有点像

std::vector<double> sub_section_of_data = data[0..2] // Obviously pseudocode!

好的,所以上面的内容是荒谬的,因为可能实现该语法的语言仍然会进行复制操作,这可能会很慢 - 这正是我试图避免的......

所以是的,我不确定在这里进行的最佳方法是什么 - 任何人都可以提出一个“好的”解决方案吗? (以非主观方式!)

编辑:为了明确这一点 - 问题是我不想做这样的事情:

std::vector<double> r1_temp;
r1_temp.push_back(data[0]); // Copy ! Bad !
r1_temp.push_back(data[1]);
r1_temp.push_back(data[2]);

... same for an r2 ...

std::vector<double> force = magic_gravity_formula(r1, r2, m1, m2);

编辑 2:考虑编译器选项 - 编译器是否会通过以下方式更改函数以接受参数来为我优化我的代码:

std::vector<double> super_gravity_formula(double x1, double y1, double z1, double x2, double y2, double z2, double m1, double m2)

在这种情况下,也许这个问题并不重要? (除了形成“让你的代码看起来很好读”的观点。)

编辑 3:因此它仍然很重要。

【问题讨论】:

  • 您是否考虑过使用移动语义?
  • 我没有,你说的这些是什么?
  • 我可以使用指向原始数据的指针。 - 这听起来像是一个计划。
  • 本质上该函数需要使用“包含所有数据的大向量”中的数据 - 但是这些东西必须以三个对象的块的形式传递,例如传递整个数组作为参考 - 这行不通!
  • 我认为你应该先分析一下,然后再决定什么更快。如果您的向量在大小上只有 6 个元素,则复制可能比通过以下指针可能使 CPU 缓存的部分无效更快。

标签: c++ c++11 vector slice array-view


【解决方案1】:

您想要查看现有矢量。

std::experimental::array_view&lt;T&gt;,或者自己动手。

数组视图是一对T*,以及让您将其视为数组的访问器operator[].begin().back()size()empty()等。

Here is one of many such implementations I've rolled。那个有点重,有一个range&lt;Iterator&gt; 视图自适应地处理随机访问迭代器,还有一个array_view&lt;T&gt; 继承自range&lt;T*&gt;

如果它不起作用,请在“yakk”上搜索另一个帖子,其中包含“array_view”一词或自行调试。我已经写了至少六次,或多或少都在调试,使用不同的const 正确性规则。

一旦你拥有它,{ vec.data()+index, vec.data()+index+3 } 将构造一个 array_view&lt;double&gt;,开销几乎为零。

【讨论】:

  • 你知道 {first,last} 初始化约定是否与提议的std::string_view 和朋友一致吗? (boost::string_ref 似乎已经和{begin, length} 一起消失了)
  • 嗯,这是一组复杂的代码,我会试着理解它
  • @sehe 我在日常工作中使用的array_view 支持{first, length} {first, last},因为我发现两者都很有用。 first, length 在处理遗留代码时非常棒,first, last 让我更容易做子视图。
  • @T.C.当然可以,但是官方实验和非官方之间的实际区别是什么?版本稳定性增加了非常轻微的程度,并且您可能已经在系统上支持投票的东西?总之,这里有一个包含array_view的提案,不知道是不是最新的:open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4512.html
  • @user 如前所述,这是一个过于复杂的。真的,您只需要 T* b=0; T* e=0; 然后编写一些您需要的简单访问器。我也喜欢能够从类似数组的构造隐式转换,但这是很多样板。
【解决方案2】:

如果你想要的是 vectors,在数学意义上,6 个 double 类型的元素,std::vector&lt;double&gt; 可能不是最有效的表示。我会继续定义一个 3D 点,然后将位置 + 速度定义为:

struct point {
   double x, y, z;
};
struct position_and_velocity {
   point pos;
   point vel;
};

对这种类型进行操作可能比对向量进行操作(无动态分配)更有效,您可以将其作为进行操作。这个结构的完整副本可能比将第一个元素插入std::vector 更便宜,并且比提供两个指向数组的指针来获得 slice 慢一点,但并不多。

此外,它更不容易出错,因为在 slicing 数组的情况下,您可能会错误地使用错误的值并最终得到类似(伪代码)slice[1..3] 之类的内容,即两个位置坐标与速度坐标之一(可能没有意义)。

函数签名是程序二进制接口的一部分,编译器不会更改函数签名,除非它内联代码。此外,我非常怀疑编译器是否会在任何转换中删除向量固有的动态分配(除非它可以删除整个变量)。

【讨论】:

  • 这将是一个很好的解决方案,但不幸的是,我有其他事物的 ODE,这些事物在维度大于或小于 3(或 6)的空间中对单个变量和向量进行操作......所以这种方法可以被使用,但需要我用垃圾“填充”这些结构中未使用的数据......这可能不安全,并且肯定会使代码混乱!
【解决方案3】:

由于代码需要const std::vector&lt;double&gt; &amp;,所以你不能更聪明。

否则(如果库函数更通用)您可以轻松地传递一个范围(参见例如 Boost Range,但您可以轻松地一次性使用一个向量子范围,就像 boost::string_refstd::string_view

【讨论】:

  • "const double * const" 那么是什么?
  • 等等。能不能换个界面? (我认为magic_gravity_formula 是......魔法,你无法改变它)
  • 回答这个问题:不,永远不要自愿放弃原始指针。请参阅@Yakk 的回答。使用微型包装器对语义进行编码(然后可以查看和检查)。这将改善你的生活,编译器会优化它
【解决方案4】:

'我可以使用指向原始数据的指针来代替......并且只需将指针传递给 x 坐标'。

你自己回答了你的问题。 std::algorithms 什么都不做,否则。

【讨论】:

  • 发表评论来解释反对票是一件不错的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-14
  • 2014-06-12
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
相关资源
最近更新 更多