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

时间:2012-02-29 06:37:06

标签: c++ functional-programming c++11

假设我有以下内容:

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

我可以写下以下内容:

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; }

所以我可以做以下事情:

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

这不需要point的副本,只需要两个结构,即point{1,2,3}的构造和point{4,5,6}的构造。即使point_addpoint_multpoint{...}位于不同的编译单元中(即无法内联),我相信这也适用。

但是,我想用更实用的方式编写代码:

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

如何编写point_multpoint_add以便不需要副本(即使point_multpoint_add位于不同的编译单元中),或者强制使用功能样式在C ++中效率不高?

5 个答案:

答案 0 :(得分:4)

让我们忽略问题的隐含谬误(即自动复制意味着效率降低)。而且我们也忽略了任何复制是否会实际发生的问题,或者它是否会被任何半合适的编译器所剔除。让我们看看它的面值:这可以不用复制就完成吗?

是的,它可能是r值引用的唯一其他合法用法(尽管之前被忽略的规定使这个用例可疑):

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

当然,这只会影响到临时工。所以你需要一个l值的替代版本:

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

重点是你可以原样传递引用,作为对临时值的引用或对l值的引用。

答案 1 :(得分:3)

可以使用非常难看的模板元编程来完成。例如,eigen使用模板,因此像matrix1 + matrix2 * matrix3这样的表达式不需要创建任何临时值。关于它如何工作的要点是矩阵的+*运算符不返回Matrix个对象,而是返回某种矩阵表达式对象,该对象在表达式的类型上被模板化PARAMATERS。然后,此矩阵表达式对象只能在需要时计算表达式的一部分,而不是创建临时对象来存储子表达式的结果。

实际上实现这个可能会非常混乱。如果您有兴趣,请查看Eigen的来源。 Boost的uBlas也做了类似的事情,尽管它并不像eigen那么广泛。

答案 2 :(得分:2)

高效(和通用)技术是表达模板。您可以阅读一个很好的介绍性解释here

很难实现并且基于模板,你不能使用单独的编译单元,但它非常有效。符号计算中一个有趣的应用是解析:Boost.Spirit构建非常有效的解析器。

C ++ 11 auto 关键字有助于在实际编程任务中使用,就像处理复杂类型时一样,请参阅this其他答案。

答案 3 :(得分:2)

首先,为什么不使用“更好”的功能?

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;
  }
};

现在it can be used as

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

但我真的认为你在这里(以及表现)过多地担心副本。

  1. 让它发挥作用
  2. 快点
  3. 如果你没有任何已经发挥作用的东西,那么谈论表现就像追逐工厂......我们认为它们很少出现瓶颈。

答案 4 :(得分:-1)

point_mult()的定义更改为:

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

并将其命名为:

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

涉及无复制。但是,您必须稍后执行delete &p;才能释放内存。