How to override operator+ in subclass - c++

I have CharRow class with such fields :
protected:
char* ptr;
int ROW_MAX_LENGTH;
And have subclass BigIntegerNumber (char array of numbers).
My operator+ in CharRow :
virtual CharRow operator+ (CharRow row2)
{
int row1Length = this->getRowCurrentLength();
int row2Length = row2.getRowCurrentLength();
CharRow tempRow(row1Length + row2Length);
for(int i = 0; i < row1Length; i++){
tempRow.ptr[i] = this->ptr[i];
}
for(int i = 0; i < row2Length; i++){
tempRow.ptr[i + row1Length] = row2.ptr[i];
}
return tempRow;
}
What do I need to invoke operator+ polymorphically ?
BigIntegerNumber operator+ (BigIntegerNumber row2)
{
BigIntegerNumber temp(this->getRowCurrentLength() + row2.getRowCurrentLength());
temp = BigIntegerNumber::addValue(*this, row2);
return temp;
}

Virtual operator + can work in general, however for it to work, some constraints must be met.
The first reason why it would not work in your case is, that the operator
BigIntegerNumber operator+ (BigIntegerNumber row2)
is not an override of the
virtual CharRow operator+ (CharRow row2)
but it is its overload (hides the original operator instead of overriding it).
For it to be the override, the function signatures would have to be the same. I.e. the same types of parameters (CharRow and not BigIntegerNumber, also better to pass by const ref than by value), and the same or covariant return type. Neither of those are met.
Things like this are sometimes done by using a "regular" virtual function taking interface references as parameters, and implementing non-virtual operators by calling such func.
In this case the main issue is the return parameter, as you cannot define the return type as by-value CharRow and actually return BigIntegerNumber (it would be sliced to the return type). You might be more lucky with the operator +=, which can return the reference to itself thus be able to work polymorphically.
The example for operator += (which does not have the problem with the return value type):
#include <iostream>
using namespace std;
struct Base
{
virtual Base& operator +=(const Base& other); // takes Derived as well for the virtual calls
};
struct Derived: Base
{
Derived& operator +=(const Base& other); // override - called via virtual
Derived& operator +=(const Derived& other); // overload - not called via virtual
// remove to always call the polymorphic version
};
Base& Base::operator +=(const Base& other)
{
cout << "Base::operator +=(Base)";
// beware this is slow!
const Derived* d = dynamic_cast<const Derived*>(&other);
if (d)
cout << " - called with Derived";
else
cout << " - called with Base";
cout << endl;
return *this;
}
Derived& Derived::operator +=(const Base& other)
{
cout << "Derived::operator +=(Base)";
// beware this is slow!
const Derived* d = dynamic_cast<const Derived*>(&other);
if (d)
cout << " - called with Derived";
else
cout << " - called with Base";
cout << endl;
return *this;
}
Derived& Derived::operator +=(const Derived& other)
{
cout << "Derived::operator +=(Derived)" << endl;
return *this;
}
int main()
{
Derived d1, d2;
Base b, b0;
Base& b1 = d1;
Base& b2 = d2;
d1 += d2; // Derived::operator +=(Derived)
b1 += d2; // Derived::operator +=(Base) - called with Derived
d1 += b1; // Derived::operator +=(Base) - called with Derived
b1 += b2; // Derived::operator +=(Base) - called with Derived
b += d2; // Base::operator +=(Base) - called with Derived
d1 += b; // Derived::operator +=(Base) - called with Base
b += b0; // Base::operator +=(Base) - called with Base
b1 += b; // Derived::operator +=(Base) - called with Base
return 0;
}
For the operator + the result type passed by value is the problem. However, in C++ still not impossible, but you then need to use some kind of wrapper. An example of such a wrapper:
#include <iostream>
#include <memory>
using namespace std;
struct Base;
struct Derived;
class BaseWrapper
{
shared_ptr<Base> _obj;
public:
explicit BaseWrapper(const shared_ptr<Base>& obj) : _obj(obj)
{}
template<class RESULT_T>
operator RESULT_T()
{
// throws if type not correct
return dynamic_cast<RESULT_T&>(*_obj);
}
Base& operator +=(const Base& other);
BaseWrapper operator +(const Base& other) const;
};
struct Base
{
virtual Base& operator +=(const Base& other); // takes Derived as well for the virtual calls
BaseWrapper operator +(const Base& other) const;
private:
virtual shared_ptr<Base> copy() const
{
return make_shared<Base>(*this);
}
};
struct Derived : Base
{
Derived& operator +=(const Base& other); // override - called via virtual
private:
virtual shared_ptr<Base> copy() const
{
return make_shared<Derived>(*this);
}
};
Base& BaseWrapper::operator += (const Base& other)
{
return *_obj += other;
}
BaseWrapper BaseWrapper::operator +(const Base& other) const
{
return *_obj + other;
}
BaseWrapper Base::operator +(const Base& other) const
{
BaseWrapper result(copy());
result += other;
return result;
}
int main()
{
Derived d1, d2;
Base b, b0;
Base& b1 = d1;
Base& b2 = d2;
b = b1 + b2; // add Derived + Derived, result is Derived (typed Base)
b = b0 + d1; // add Base + Derived, result is Base
// d1 = b0 + d1; // add Base + Derived, result is Base, throws bad_cast (cannot cast to Derived)
d1 = b1 + b2; // add Derived + Derived, result is Derived
return 0;
}
That is, the BaseWrapper can be used to return the polymorphic type by value, and have conversions to the original type. But also note that in this case the memory allocation is involved.

If you want to invoke CharRow::operator+() inside BigIntegerNumber::operator+() you can do it as:
BigIntegerNumber operator+(BigIntegerNumber row2)
{
return CharRow::operator+(row2);
}

Related

How does copy constructor that returns value, discards the temp?

having this code:
#include <iostream>
class Base {
public:
Base() = default;
explicit Base(int val) : _var(val) {}
Base operator=(const Base &rhs) {
_var = rhs._var;
return *this;
}
void print() const {
std::cout << _var << std::endl;
}
private:
int _var;
};
int main() {
Base b[] = {Base(10), Base(), Base(), Base()};
(b[1] = b[2]) = b[0];
for (Base base: b) {
base.print();
}
}
the output is:
10
0
0
0
but I would expect
10
10
0
0
As the second element in array b[1] should get assign from b[0], but the assignment operator returns value, not reference and thus copy-constructing happen. But still, why is not b[1] copy-constructed to have _var=10?
If the operator= returned Base &, the output would be my expectation
To get the desired result of your assignment operator (which, by the way, is different from copy constructor), you need to return a reference:
Base& operator=(const Base &rhs)
This is the canonical form.
Without the reference, the result of (b[1] = b[2]) is stored in a temporary. (b[1] = b[2]) = b[0]; assigns to that temporary, which is discarded and has no effect on b[1].

derived class reusing base class operations

I have a large class, Base, that implements several different binary operations, some overloading the operators like *=, *, / etc, and some extra ones. The typical return type involves Base explicitly, for example:
class Base {
private:
std::vector<unsigned int> _arr;
public:
// These operations do something with _arr
Base& operator *= (const Base& rhs);
friend Base operator *(Base lhs, const Base& rhs);
std::vector<Base> myoperation(const Base& rhs);
};
I have a derived class, Derived, which simply adds extra structure, so it looks like this:
class Derived : public Base {
private:
std::vector<int> _arr_derived;
public:
// This operation does something with _arr and _arr_derived;
Derived& my_derived_operation(const Derived& rhs);
// These operations are exactly the same implementation as the one in Base!
Derived& operator *= (const Derived& rhs) {
Base::operator*=(rhs);
return *this;
}
friend Derived operator *(Derived lhs, const Derived& rhs) {
lhs *= rhs;
return lhs;
}
std::vector<Derived> myoperation(const Derived& rhs) // <---- what to do here?
};
What do I need to do with the operation myoperation? That returns a vector. There is no extra logic on all operations that are inherited, the only difference is in the return type. Is there any way to avoid the code duplication? Even the declaration of the operations is bothersome.
I thought perhaps defining the base operations as template functions returning the same type as the argument, and requiring the template parameter to be a derived class:
class Base {
// on C++20
template<class C> requires std::is_base_of_v<Base,C>
C& operator*=(const C& rhs);
}
Does this work? Even if it does, is there a better mechanism?
Edit to reply to Remy Lebeau's comment: I want to emphasize that all operations that appear both in Derived and in Base are meant to be the same, there is not extra logic except the returning type. The example of operator *= already shows this, just calling the Base version. A possible implementation, albeit stupid, of what I mean by myoperation could be this:
std::vector<Base> Base::myoperation(const Base& rhs) {
std::vector<Base> ret{};
ret.push_back(rhs*rhs);
return ret;
}
std::vector<Derived> Derived::myoperation(const Derived& rhs) {
std::vector<Derived> ret{}
ret.push_back(rhs*rhs);
return ret;
}
With the templated version I could write only
class Base {
teplate<class C> requires std::is_base_of_v<Base,C>
auto myoperation(const C& rhs) {
std::vector<C> ret;
ret.push_back(rhs*rhs);
return ret;
}
};
And that's what I meant by the templated solution above, so that I do not need to even declare myoperation on Derived.
I went with the templated version, although using pointers is standard in polymorphism, I have too many vector valued operations and are complicated enough to change the logic, and also wouldn't want to have to deal with recasting to Derived classes when necessary. Templating also has the advantage that no code needs to be written on the derived classes. So on base all operations that transform the object in place are left as they were and the ones that produce new ones are templated. I appreciate if I could be pointed to pitfalls of this approach
#include <iostream>
#include <vector>
class Base {
int _a;
public:
Base(int a) : _a{a} {};
Base& operator *= (const Base& rhs) {
_a *= rhs.a();
return *this;
}
template <class C> requires std::is_base_of_v<Base,C>
friend C operator *(C lhs, const Base& rhs) {
lhs *= rhs;
return lhs;
}
template <class C> requires std::is_base_of_v<Base,C>
std::vector<C> myop (const C& rhs) {
std::vector<C> ret {};
ret.push_back(rhs*rhs);
return ret;
}
int a() const { return _a; }
};
class Derived : public Base {
int _b;
public:
Derived(int a, int b) : Base{a}, _b {b} {}
int b() const {return _b;}
};
int main() {
Derived C{9,5};
Derived D{4,7};
auto E = C*D;
auto F = D.myop(C);
std::cout << E.a() << ", " << E.b() << std::endl;
std::cout << F.front().a() << ", " << F.front().b() << std::endl;
return 0;
}
OUTPUT:
36, 5
81, 5

What is the difference between `void operator=(T&&)` and `T& operator=(T&&)`?

class base
{
public:
base(const base&) = delete;
base()
{
cout << "construct" << endl;
}
~base()
{
cout << "destruct" << endl;
}
int a;
int b;
/* The difference explanation I desired is here */
void operator=(base&& other)
// base& operator=(base&& other) // this needs to collaborate with "return *this"
{
this->a = other.a;
this->b = other.b;
// return *this;
}
/* Not here */
base& operator=(base& other) = delete;
};
What is the difference between the two versions of operator=(T&&)? They seem both work to me. However, as class member function, the website recommand base& operator=(T&&) version.
In one case, a=b=c works. In the other, it does not.
That is it.
Traditionally, a=b=c does b=c then assigns the result to a. If your operator= returns void, it instead fails to compile.

Clearing a class instance with a member that has a deleted assignment operator

I wanted to clear / reinstansiate a instance of a class using the assignment operator, but some members in that class have their assignment operator deleted. So when i try to assign it to a new instance it keeps its old values.
Heres a example:
#include <cstdio>
class C
{
C operator= (const C&) = delete;
};
class B
{
public:
int x = 0;
C c;
B& operator=(const B& other)
{
return B();
}
};
int main()
{
B b;
b.x = 5;
b = B();
printf("%i\n", b.x); // prints 5, should print 0
return 0;
}
Is there some simple workaround for this without writing a method that clears all of its members? Why does this happen?
Why does this happen?
Your current implementation of operator=() is fubar.
B& operator=(B const &other)
{
x = other.x;
return *this;
}
you should also test for self-assignment, before you do anything, though, since copying members can be quite expensive:
B& operator=(B const &other)
{
if(this != &other)
x = other.x;
return *this;
}

How to understanding the meaning of C++03 13.5.3/2

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
struct B {
virtual int operator= (int rhs)
{
m_iValue = rhs;
return m_iValue;
}
virtual B& operator= (const B& rhs)
{
m_iValue = rhs.m_iValue;
return *this;
}
int m_iValue;
};
struct D : B {
virtual int operator= (int rhs)
{
m_iValue = rhs;
return m_iValue;
}
virtual D& operator= (const B& rhs)
{
m_iValue = rhs.m_iValue;
return *this;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
D dobj1;
D dobj2;
B* bptr = &dobj1;
bptr->operator=(99); // calls D::operator=(int)
*bptr = 99; // ditto
bptr->operator=(dobj2); // calls D::operator=(const B&)
*bptr = dobj2; // ditto
dobj1 = dobj2; // calls implicitly-declared
// D::operator=(const D&)
return 0;
}
Question 1> This question maybe is related to the question 2&3.
Reference: C++03 13.5.3/2
Note: for a derived class D with a base class B for which a virtual
copy assignment has been declared, the copy assignment operator in D
does not override B’s virtual copy assignment operator.
What does the following statement mean in plain English?
the copy assignment operator in D does not override B’s virtual copy
assignment operator.
Question 2> why the following statement call `D::operator=(int)
*bptr = 99; // ditto
Question 3> Why the following statement call implicit D::operator=(const D&)
dobj1 = dobj2; // calls implicitly D::operator=(const D&)
Question 1.
For B, the copy assignment operator is B::operator=(const B&) or similar. For D it's D::operator=(const D&). One cannot override the other because argument types are different.