高效的函数式(C++11可以)
Efficient functional style (C++11 ok)
假设我有以下内容:
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++中被迫没有那么高效?
让我们忽略这个问题隐含的谬误(即自动复制意味着效率降低)。我们也可以忽略这样一个问题,即是否真的会发生任何复制,或者是否所有复制都会被任何半吊子的编译器所消除。让我们从表面上看:这能在不复制的情况下完成吗?
是的,它可能是r值引用的唯一其他合法使用(尽管之前被忽视的规定使这个用例变得可疑):
point &&point_mult(point &&p, double c);
当然,这只适用于临时措施。所以你需要一个l值的替代版本:
point &point_mult(point &p, double c);
关键是,您可以按原样传递引用,既可以作为对临时值的引用,也可以作为对l值的引用。
这可以用非常难看的模板元编程来完成。例如,eigen使用模板,这样像matrix1 + matrix2 * matrix3
这样的表达式就不需要创建任何临时性。其工作原理是矩阵的+
和*
运算符不返回Matrix
对象,而是返回某种基于表达式参数类型模板化的矩阵表达式对象。然后,这个矩阵表达式对象只能在需要时计算表达式的部分,而不是创建临时对象来存储子表达式的结果。
实际上,实现这一点可能会变得相当混乱。如果你感兴趣的话,可以看看艾根的来源。Boost的uBlas也做了类似的事情,尽管它没有本征那么广泛。
一种有效(通用)的技术是表达式模板。你可以在这里阅读一个很好的介绍性解释。
它很难实现,而且基于模板,不能使用单独的编译单元,但它非常高效。符号计算中一个有趣的应用是解析:BoostSpirit用它们构建了非常高效的解析器。
C++11auto关键字有助于在实际编程任务中使用,与处理复杂类型时一样,请参阅其他答案。
首先,为什么不使用"更好"的函数?
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});
但我真的认为你太担心这里的副本(和性能)了。
- 让它发挥作用
- 快一点
如果你没有任何已经有效的东西,谈论性能就像追逐工厂。。。瓶颈很少在我们想象的地方。
将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;
以释放内存。
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- 为什么即使调用了析构函数,C++11 中的分离线程也可以执行
- 是否可以使用 DirectX 3D 11 绘制由三角形组成的圆?
- C++11:具有互斥锁的线程看到原子变量的值发生变化,尽管这是唯一可以改变它的代码
- C ++枚举范围无法使用-std=c ++ 98进行编译,但使用-std=c ++ 11可以
- 是否可以仅使用标准 c++/c++11 实现不带"sleep"的计时器?
- C++11 - 可以等待多个不同事件的线程?
- C++11:我可以显式调用基类析构函数来销毁派生类吗
- std::is_invocable可以在 C++11 中模拟吗?
- 是否可以通过使用移动/交换 c++11 来延长返回的临时变量的生命周期
- 是否可以在 64 位系统中为 11 位系统中的图形创建位邻接数组
- 当加入 C++11 函数的线程仍未终止时,是否可以返回?
- C++11 中的随机数:有没有一种简单的方法可以将生成器种子放在代码的一个位置,然后在不同的函数中使用它?
- 是否有一个很好的方法可以在C 11中打印出像JSON一样的Trie结构(仅迭代解决方案)的扁平命名空间
- 摘要类可以在C 11中具有构造函数和私人成员
- C 11:可以默认默认构造函数导致部分初始化类
- 我可以在 c++11 中的 std::d eque 上使用 std::max_element() 吗?
- 在C 11或更高版本中,是否可以通过Lambda来实现单方法纯Virtual C 接口
- 并发 C++11 - 可以使用哪些工具链
- 高效的函数式(C++11可以)