假设我有以下内容:
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_add
,point_mult
和point{...}
位于不同的编译单元中(即无法内联),我相信这也适用。
但是,我想用更实用的方式编写代码:
point p = point_add(point_mult(point{1,2,3}, 2), point{4,5,6});
如何编写point_mult
和point_add
以便不需要副本(即使point_mult
和point_add
位于不同的编译单元中),或者强制使用功能样式在C ++中效率不高?
答案 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;
}
};
Point p = ((Point{1,2,3} *= 2) += Point{4,5,6});
但我真的认为你在这里(以及表现)过多地担心副本。
如果你没有任何已经发挥作用的东西,那么谈论表现就像追逐工厂......我们认为它们很少出现瓶颈。
答案 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;
才能释放内存。