c++:重载操作符的后续操作

C++: about subsequent operations of overloaded operator

本文关键字:操作 操作符 重载 c++      更新时间:2023-10-16
#include <iostream>
#include <vector>
using namespace std;

void testfn(double* v1, double *v2, double *v3, int n);//I must use this function
class CLS{
private:
    vector<double> v;
public:
    CLS(vector<double> vin);
    CLS operator+(CLS & A);
    CLS operator*(CLS & A);
};
CLS::CLS(vector<double> vin)
{
    v=vin;
}
CLS CLS::operator*(CLS &A){
    //assuming the two vectors have the same length
    vector<double> vtmp(v.size(),0);
    testfn(&*A.v.begin(),&*v.begin(),&*vtmp.begin(),(int)A.v.size());
    CLS C(vtmp);
    return C;
}
CLS CLS::operator+(CLS &A){
    //assuming the two vectors have the same length
    vector<double> vtmp(v.size(),0);
    testfn(&*A.v.begin(),&*v.begin(),&*vtmp.begin(),(int)A.v.size());
    CLS C(vtmp);
    return C;
}
void testfn(double* v1, double *v2, double *v3, int n)
{
    for (int i=0;i<n;i++)
    {
        *v3=*v1+*v2;
        ++v1;
        ++v2;
        ++v3;
    }
}
int main(){
    vector<double> v1(100,1.0), v2(100,2.0), v3(100,0.0);
    CLS C1(v1),C2(v2),C3(v3);
    CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;
    return 0;
}

我创建了类CLS并定义了两个操作符+和*。我想使用这些操作符,就像我们对整数和双精度浮点数使用+和*一样简单。因此,我在主CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;中有一条测试线。然而,在使用gcc编译这段代码时,我得到了大量的错误。我对操作符重载的规则不太熟悉。我应该如何修改*和+的定义(也许只是参数?),使CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;是有效的?

补充:我不知道操作符+和*的const形参如何使用,因为这两个操作符的定义涉及到另一个函数testfn,它的形参是double*而不是const double*。此外,我不想改变testfn定义中的任何部分,因为在我的实际代码中,它对应于LAPACK中的一个函数,我绝对没有权利改变它。

最好将operator+operator*定义为友元函数,这样,如果存在从其他类型到CLS的隐式转换,则可以使用操作符。如果它是一个成员函数,那么如果第一个操作数不是CLS,它将找不到。

friend CLS operator+(CLS lhs, const CLS& rhs) 
{
  // Do your logic here
  return lhs; 
}

你应该在你的代码中使用空格,这是可怕的阅读。不要在类型名和变量中都使用大写字母,那样读起来也很可怕。

这不是必需的,但这是一个小的优化,通过引用传递构造函数参数,然后在构造函数init列表中初始化v成员(而不是在主体中对其赋值):

CLS::CLS(const vector<double>& vin) : v(vin)
{
}
我将添加一个新的构造函数,它接受所需的大小,并立即将v成员的大小设置为该大小,这使下面的代码更简单:
CLS::CLS(int n) : v(n, 0)
{
}

首先将const添加到函数形参和成员函数本身:

CLS CLS::operator*(const CLS& a) const {
                   ^^^^^         ^^^^^

然后为结果默认构造一个空的C对象,具有所需大小的vector成员:

    //assuming the two vectors have the same length
    CLS result(v.size());

如果创建了数据向量的副本,并对这些副本进行操作,则可以使成员函数及其实参为const:

    vector<double> v1 = a.v;
    vector<double> v2 = v;

现在你可以获得指向这些向量中的数据的非const指针,并且你可以直接将输出写入C.v,而不是写入vtmp,然后将其复制到返回的对象中:

    testfn(&*v1.begin(), &*v2.begin(), &*result.v.begin(), v.size());

最后返回结果:

    return result;
}

另一个函数的变化完全相同:

CLS CLS::operator+(const CLS& a) const {
    //assuming the two vectors have the same length
    CLS result(v.size());
    vector<double> v1 = a.v;
    vector<double> v2 = v;
    testfn(&*v1.begin(), &*v2.begin(), &*result.v.begin(), v.size());
    return result;
}

现在这将能够接受const参数,因此应该与每个C1+C2C3*C4表达式创建的临时对象一起工作。

由于数据的额外副本,代码的效率略低,但构造函数中的优化有助于此。如果你能够使用c++ 11,即使这些低效率也可以解决,但需要更多的工作。

首先,c++ 11提供了vector<T>::data()来获取指针,这比&*v.begin()要清晰得多。(在c++ 11之前,它不是c++标准的一部分,但GCC甚至在c++ 98中提供了它)。您可以将上面显示的代码更改为使用v.data()而不是&*v.begin(),如下所示。

在c++ 11中,可以通过将(非const) 右值引用绑定到临时对象,从而为临时对象添加额外的重载。因为右值引用允许对临时对象进行非const访问,所以不需要复制它们的数据来获取非const指针,而是可以直接使用它们。除了上面所示的操作符重载之外,还可以添加这些操作符重载:

CLS CLS::operator+(CLS&& a) && {
    //assuming the two vectors have the same length
    CLS result(v.size());
    testfn(a.v.data(), v.data(), result.v.data(), v.size());
    return result;
}
CLS CLS::operator*(CLS&& a) && {
    //assuming the two vectors have the same length
    CLS result(v.size());
    testfn(a.v.data(), v.data(), result.v.data(), v.size());
    return result;
}

这避免了在算术表达式创建的中间临时对象中进行任何复制。我还发现更容易理解,更容易阅读。

一个完整的工作示例在http://coliru.stacked-crooked.com/a/6e40d20eb7f1e9fc

使用testfn与所有非const参数的不寻常要求有点奇怪,但它是…嗯. .勉强接受对于复杂表达式不接受——testfn的前两个参数必须是const double*

基本原理: c++标准要求临时绑定到const references -这里有一些有趣的细节

@Resorter说:

"谢谢你让我知道,我想学习。但是你确定能解决我的问题吗?我真的想先解决这个问题。"

[where it is 'move constructor' and 'move assign ']


下面的"fish"假设你已经阅读/学习了fishing move constructor和move assign以及5/3/0的规则,

补充学习材料:拷贝省略-当编译器被允许跳过拷贝/移动构造函数时,即使它们会产生副作用。

// some implementations
void testfn0(const double* v1, const double *v2, double *v3, int n) {
  for(int i=0; i<n; i++) {
    v3[i]=v1[i]+v2[i];
  }
}
void testfn1(double* v1, double *v2, double *v3, int n) {
  for(int i=0; i<n; i++) {
    v3[i]=v1[i]*v2[i];
  }
}
class CLS {
  std::vector<double> v;
public:
  // constructor taking a const reference
  CLS(const std::vector<double>& in) : v(in) {
    std::cout << "copy vec" << std::endl;
  }
  // extra constructor with rvalue reference: it will "canibalize" the parameter
  CLS(std::vector<double>&& in) : v(in) {
    std::cout << "move vec" << std::endl;
  }
  // Rule of 5 applied
  // Copy constructor
  CLS(const CLS& other) : v(other.v) {
    std::cout << "copy CLS" << std::endl;
  }
  // Move constructor
  CLS(CLS&& other) : v(std::move(other.v)) {
    std::cout << "move CLS" << std::endl;
  }
  ~CLS() { }
  // Copy assgn
  CLS& operator=(const CLS& o) {
    this->v=o.v;
    std::cout << "assgn CLS" << std::endl;
    return *this;
  }
  // Move assgn
  CLS& operator=(CLS&& other) {
    this->v=std::move(other.v);
    std::cout << "move CLS" << std::endl;
    return *this;
  }
  // WILL NOT WORK WITHOUT const FOR COMPLEX EXPRESSIONS
  //                       |
  //              ----------
  //              V
  CLS operator+(const CLS& rhs) {
    std::vector<double> vtmp(v.size(),0);
    testfn0(&*rhs.v.begin(),&*v.begin(),&*vtmp.begin(),(int)rhs.v.size());
    CLS ret(std::move(vtmp));
    return ret;
  }
  // WILL NOT WORK WITHOUT const FOR COMPLEX EXPRESSIONS
  //                       |
  //              ----------
  //              V
  CLS operator*(const CLS& rhs) {
    std::vector<double> vtmp(v.size(),0);
    testfn1(&*rhs.v.begin(),&*v.begin(),&*vtmp.begin(),(int)rhs.v.size());
    CLS ret(std::move(vtmp));
    return ret;
  }
};
int main() {
  std::cout << "n--- inits " << std::endl;
  CLS a(std::vector<double>{1,2,3}); // move vec
  CLS b(std::vector<double>{3,2,1}); // move vec
  std::cout << "n--- add " << std::endl;
  CLS c=a+b; // move vec and 'silence' (copy elision)
  std::cout << "n--- mul " << std::endl;
  CLS d=a*b; // move vec and 'silence' (copy elision)
  std::cout << "n--- copy " << std::endl;
  CLS m=c; // Copy CLS - constructor
  std::cout << "n--- move " << std::endl;
  CLS n=std::move(d); // Move CLS - constructor
  std::cout << "n--- assgn (d=c) and copy (x=c)" << std::endl;
  CLS x=d=c;
}