Resorter Resorter - 1 month ago 17
C++ Question

C++: about subsequent operations of overloaded operator

#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;
}


I create class CLS and defined two operators + and *. I want to use these operators as simply as how we use + and * for integers and doubles. Therefore I have a test line in the main
CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;
. However I get tons of errors when compiling this code using gcc. I am not familiar enough with the rules of operator overloading. How should I modify the definition (maybe just parameters?) of * and + so that
CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;
is valid?

Supplements: I don't know how to use const parameter for operator+ and * , since the definition of these two operators involve another function
testfn
, whose parameters are double* instead of const double*. Further more, I do not want to change any part in the definition of
testfn
because in my real code it corresponds to a function in LAPACK which I absolutely have no right to change.

Answer

The unusual requirement to use the testfn with all params non-const is a bit weirds, but it's... umm.. reluctantly acceptable inacceptable for the case of complex expressions - the first two params of the testfn need to be const double*.

Rationale: C++ standard requires temporaries to be bound to const references - some interesting details here

@Resorter says:

"Thanks for letting me know and I would like to learn. But are you sure it solves my problem? I really want to solve this problem first."

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


The following "fish" assumes you have read/learnt fishing move constructor and move assign and the rules of 5/3/0,

Supplementary study material: copy elision - when the compiler is allowed to skip the copy/move constructors even when they would have side effects.

// 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;
}