-1

I am trying to define an immutable object FRACTION. Because NOMINATOR and DENOMINATOR defines the object that is created, I make them const.

class Fraction{
  const int nominator, denominator;
public:
  Fraction(int nominator, int denominator):nominator(nominator), denominator(denominator){}
  Fraction(const Fraction& copy): nominator(copy.nominator), denominator(copy.denominator){
      // no need to check for a denominator of 0 here since copy must already be a valid Fraction
      std::cout << "Copy constructor called\n"; // just to prove it works
  }

  Fraction Multiply(Fraction frac){
      return Fraction(nominator * frac.nominator, denominator * frac.denominator);
  }

  string toString(){
      return "[" + to_string(nominator) + "/" + to_string(denominator) + "]";
  }
  static void Test();
};

void Fraction::Test(){
    cout << endl << "--- TEST: Fraction ----------------------------------------------" << endl;
    Fraction fracA = Fraction(2, 1);
    Fraction fracB = fracA;
    cout << "Before multiplying: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << endl;
    Fraction fracC = fracB.Multiply(fracB);
    cout << "After multiplying: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << ", fracC=" << fracC.toString() << endl;

    //--Update fracB using itself
    //fracB = fracB.Krat(fracB);
    //cout << "After changing fracB: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << ", fracC=" << fracC.toString() << endl;
}

Everything seems to work fine except when I try:

fracB = fracB.Multiply(fracB);

then I get an error:

error: object of type 'Fraction' cannot be assigned because its copy assignment operator is implicitly deleted

While

fracC = fracB.Multiply(fracB);

is ok.

I thought the solution would be to deliver custom copy assignment operator, but after several attempts, I always ended up with unchanged fracB. Is there any custom copy assignment operator, that would do the job? Or the whole approach to immutable objects in C++ is wrong here?

I am aware of const-correctness: https://isocpp.org/wiki/faq/const-correctness

Khamyl
  • 398
  • 2
  • 12
  • 1
    `fracB` is not modifiable, so you can’t assign to it. `fracC` doesn’t exist, but if the code is `Fraction fracC = fracB;`, that’s okay beca use it’s not assigning to an existing object; it’s creating a new one. – Pete Becker Jan 19 '20 at 20:21
  • 1
    What you can do, is have them as non-const members. They are private, and have no modifying functions, so should be safe. – ChrisMM Jan 19 '20 at 20:28
  • A general rule is that if you provide a copy constructor you'll want to create an assignment operator as well. – Tony Jan 19 '20 at 20:45

2 Answers2

2

C++11 has deprecated the generation of a copy assignment operator if the class has a userdeclared copy constructor.

So use

Fraction& operator=(const Fraction& rhs) = default;

And remove const here

const int nominator, denominator;

How can const be assignable!?

Note that

Fraction fracC= fracB;//copy constructor calling
Fraction fracC= fracB.Multiply(fracB);//copy constructor calling twice
fracC = fracB.Multiply(fracB);//copy = operator calling
asmmo
  • 6,922
  • 1
  • 11
  • 25
  • The question was about possibilities when NOT removing the const. I know, when I remove the const, everything is working fine, even without the explicit definition of assignment operator. I was trying to simulate something like "Immutable object design pattern" in C++. – Khamyl Jan 21 '20 at 09:23
  • @Khamyl - if you want const members you can't have assignment. Which is more important to you, you can only pick one. – Tony Jan 22 '20 at 13:39
0

You can now define your own copy-assignment operator for classes that contain const member objects without undefined behavior as of c++20.

This was undefined behavior prior to c++ and remains so for complete const objects but not non-const objects with const members.

https://stackoverflow.com/a/71848927/5282154

doug
  • 3,840
  • 1
  • 14
  • 18