没有中间对象的操作的赋值
Assignment of an operation without intermediate object
给定这样的类:
class Vec{
int comp[2];
public:
void add(Vec& vec, Vec& vec2){
comp[0] = vec.comp[0] + vec2.comp[0];
comp[1] = vec.comp[1] + vec2.comp[1];
}
Vec operator+ (Vec& vec){
Vec res;
res.comp[0] = comp[0] + vec.comp[0];
res.comp[1] = comp[1] + vec.comp[1];
return res;
}
};
有两个成员函数本质上做着相同的事情,即将两个Vec
添加在一起。当然,区别在于add
函数中没有涉及中间值,而operator+
声明了Vec
的局部对象。
我的问题是,有没有办法将operator+
与operator=
结合起来定义为与add
具有相同的语义,没有中间值?其基本原理是为了提高效率,减少所涉及的中间值的数量,同时保持operator
语法的优雅。
GMP库的C++接口显然能够做到这一点:http://gmplib.org/manual/C_002b_002b-Interface-General.html#C_002b_002b-接口通用
该实现的一个重要特性是,类似a=b+c的表达式会导致对相应mpz_add的单个调用,而不会对b+c部分使用临时调用。
我想知道是否有一些可能的方法来做到这一点,或者GMP是否必须为此使用某种变通方法?
这是个坏主意,因为它违反了大多数人对操作员行为的自然假设。
不幸的是,它也非常重要(尤其是对于像gmpxx
这样的库,它们希望提供使用运算符的自然代码、高性能,并且有巨大的对象作为参数)。他们是如何做到这一点的?通过使用表达式模板,他们可以在编译时优化表达式。其思想是运算符不直接返回值,而是返回表达式类的具体实例,然后可以使用元编程对其进行操作。
表达式模板是一个相当高级的主题,您应该相对确信它会给您的代码带来显著的好处。在您的示例中,向量的大小根本不够相关。
Wiki表达式模板
我认为这没有真正的优势,因为您的对象非常小。但对于像动态分配的大向量这样的东西,这可能是有用的。
实现它的一种方法是创建一个临时noop类来保存未赋值的表达式。这个类可以转换为Vec,这样它对用户来说就透明了。关键是现在可以为add表达式创建一个专用运算符=。一些代码(填补空白):
struct VecAdd;
struct Vec
{
Vec& operator=(const VecAdd& vecadd) { /*...*/ }
};
struct VecAdd
{
const Vec& v1;
const Vec& v2;
operator Vec() { return Vec(/*...*/); } // for things like: f(v+u);
};
VecAdd operator+(const Vec& a, const Vec& b) { return VecAdd{a, b}; }
这就是GMP的实际作用。但是通过大量的宏和模板来减少代码的重复性。
operator+=
怎么样,然后用作myVec += otherVec
- C++:将值赋值给原始数据类型(例如布尔值)是原子操作吗?
- 原子变量的多重赋值是原子操作吗?
- 在C++中,当表达式涉及对象时,将表达式赋值到对象中时,是否有定义的操作顺序?
- 如何声明可以执行赋值操作的函数?(C++)
- 如何让迭代器使用赋值运算符对列表进行操作
- 为什么 GCC 拒绝复制赋值操作中的常量引用
- 为什么为单个赋值操作调用复制构造函数和重载赋值运算符
- 为什么在赋值操作完成后调用对象的析构函数
- 继承的加法赋值操作,如何返回正确的类型
- 赋值操作在C中隐式计算为什么布尔值
- 在c++中操作重载赋值运算符
- 编译器不使用移动 c'tor / 赋值操作器?
- 没有中间对象的操作的赋值
- 赋值逻辑操作的结果
- 不带赋值运算符的位NOT操作-可能
- sort() 函数中的赋值操作
- C++11当递增原子变量,并将其赋值给其他值时,就是原子操作
- 为什么此赋值操作会导致不明确的函数调用
- 拷贝赋值操作出现分段错误
- 允许/禁用模板的特定复制因子和赋值操作