没有中间对象的操作的赋值

Assignment of an operation without intermediate object

本文关键字:操作 赋值 对象 中间      更新时间:2023-10-16

给定这样的类:

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