简体   繁体   English

高效的功能风格(C ++ 11正常)

[英]Efficient functional style (C++11 ok)

Lets say I have the following: 可以说我有以下几点:

struct point
{
  double x;
  double y;
  double z;
};

I can write the following: 我可以写以下内容:

void point_mult(point& p, double c) { p.x *= c; p.y *= c; p.z *= c; }
void point_add(point& p, const point& p2) { p.x += p2.x; p.y += p2.y; p.z += p2.z; }

So I can then do the following: 因此,我可以执行以下操作:

point p{1,2,3};
point_mult(p, 2);
point_add(p, point{4,5,6});

This requires no copies of point , and only two constructions, namely the construction of point{1,2,3} and the construction of point{4,5,6} . 这不需要point副本,只需要两种构造,即point{1,2,3}的构造和point{4,5,6}的构造。 I believe this applies even if point_add , point_mult and point{...} are in separate compilation units (ie can't be inlined). 我相信,即使point_addpoint_multpoint{...}位于单独的编译单元中(即不能内联),这也适用。

However, I'd like to write code in a more functional style like this: 但是,我想以更实用的方式编写代码,如下所示:

point p = point_add(point_mult(point{1,2,3}, 2), point{4,5,6});

How can I write point_mult and point_add such that no copies are required (even if point_mult and point_add are in separate compilation units), or is functional style forced to be not as efficient in C++? 我该如何编写point_multpoint_add而不需要任何副本(即使point_multpoint_add位于单独的编译单元中),还是必须强制函数样式在C ++中效率不高?

Let's ignore the implicit fallacy of the question (namely that copying automatically means reduced efficiency). 让我们忽略这个问题的隐含谬误(即自动复制意味着降低效率)。 And let's also ignore the question of whether any copying would actually happen, or whether it would all be elided away by any half-decent compiler. 而且,我们也忽略了这样的问题:是否会真正进行任何复制,或者是否被任何半透明的编译器所取代。 Let's just take it on face value: can this be done without copying? 让我们从表面上看:不复制就可以做到吗?

Yes, and it is probably the only other legitimate use for r-value references (though the previously ignored stipulations make this use case dubious): 是的,它可能是r值引用的唯一其他合法用法(尽管先前被忽略的规定使此用例值得怀疑):

point &&point_mult(point &&p, double c);

Of course, this will only bind to temporaries. 当然,这只会绑定到临时对象。 So you would need an alternate version for l-values: 因此,您将需要用于l值的备用版本:

point &point_mult(point &p, double c);

The point is that you pass the references through as they are, either as references to temporaries or references to l-values. 关键是您可以按原样传递引用,无论是临时引用还是l值引用。

It can be done with really ugly template metaprogramming. 可以使用非常丑陋的模板元编程来完成。 For example eigen uses templates so that expressions like matrix1 + matrix2 * matrix3 don't need to create any temporaries. 例如, 本征使用模板,因此诸如matrix1 + matrix2 * matrix3类的表达式无需创建任何临时matrix1 + matrix2 * matrix3 The gist of how it works is the + and * operators for matrices don't return Matrix objects but instead return some kind of matrix expression object which is templatized on the types of the expression paramaters. 其工作原理的主旨是矩阵的+*运算符不返回Matrix对象,而是返回某种基于表达式参数类型而模板化的矩阵表达式对象。 This matrix expression object can then compute parts of the expression only when they are needed instead of creating temporary objects to store the result of subexpressions. 然后,仅当需要时,此矩阵表达式对象才可以计算表达式的各个部分,而无需创建临时对象来存储子表达式的结果。

Actually implementing this can get quite messy. 实际上实现此操作可能会变得很混乱。 Have a look at Eigen's source if your interested. 如果您有兴趣,请查看本征的来源。 Boost's uBlas also does something similar, though it's not as extensively as eigen. Boost的uBlas也做类似的事情,尽管它不像本征那样广泛。

An efficient (and generalized) technique is expression templates . 一种有效(通用)的技术是表达式模板 You can read a nice introductory explanation here . 您可以在此处阅读一个不错的介绍性解释。

It's difficult to implement and being based on templates, you cannot use separate compilation units, but it's very efficient. 很难实现并且基于模板,您不能使用单独的编译单元,但是它非常有效。 An interesting application in symbolic computation is parsing: Boost.Spirit builds very efficient parsers out of them. 解析在符号计算中一个有趣的应用是:Boost.Spirit从中构建出非常高效的解析器。

C++11 auto keywords helps usage on practical programming tasks, as always when dealing with complex types, see this other answer. C ++ 11 自动关键字有助于在实际的编程任务中使用,就像处理复杂类型时一样,请参阅其他答案。

First, why not use "better" functions ? 首先,为什么不使用“更好”的功能?

struct Point {
  double x;
  double y;
  double z;

  Point& operator+=(Point const& right) {
    x += right.x; y += right.y; z += right.z;
    return *this;
  }

  Point& operator*=(double f) {
    x *= f; y *= f; z *= f;
    return *this;
  }
};

Now it can be used as : 现在可以用作

Point p = ((Point{1,2,3} *= 2) += Point{4,5,6});

But I truly think that you worry too much about copies here (and performance). 但是我确实认为您太担心此处的副本(和性能)。

  1. Make it work 让它起作用
  2. Make it fast 快一点

If you don't have anything that already works, talking about performance is akin to chasing mills... bottlenecks are rarely where we thought they would be. 如果您还没有任何可行的方法,那么谈论性能就像追逐工厂一样……瓶颈很少出现在我们认为的情况下。

Change the definition of point_mult() to: point_mult()的定义更改为:

point& point_mult(point& p, double c) { p.x *= c; p.y *= c; p.z *= c; return p; }
^^^^^^                                                                ^^^^^^^^^

And call it as: 并将其称为:

point & p = point_add(point_mult(*new point{1,2,3}, 2), point{4,5,6});
     ^^^                         ^^^^^

there is no copy involved. 没有副本 However, you have to later do delete &p; 但是,您以后必须delete &p; for releasing the memory. 用于释放内存。

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

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