Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inherited functions to return Derived class, not Base class

Tags:

Is it possible in C++ to formulate functions in the Base class that return Base type such that in the Derived class, they return Derived type, without overloading?

Minimal example:

class Base
{
    public:
        Base(double v)
        {
            value = v;
        }

        Base add(Base b)
        {
            return Base(b.value + this->value);
        }

        void print()
        {
            std::cout << value << std::endl;
        }

        double value;
};



class Derived : public Base
{
    public:
        Derived(double v) : Base(v)
        {

        }

        void timesTwo()
        {
            value *= 2.0;
        }
};


int main()
{

    Derived d1(1), d2(2);

    // This doesn't work because the result is of type Base
    (d1.add(d2)).timesTwo();


    return 0;
}

Motivation

In the actual example, Base represents a linear algebra matrix, and Derived represents a vector. The matrix offers many functions that are all applicable to vectors, such as addition or multiplication by a scalar.

In this case, it would be desirable not having to override all these matrix functions manually to return vectors. I would like to, if possible, express that whatever this type is, the return type should be identical to it.

Example:

class Matrix
{
    ...
    Matrix operator*(double x);
};

class Vector : Matrix
{
    ...
};

Matrix M;
M = M * 2.0; // works

Vector v;
v = v * 2.0; // does not work, because v * 2.0 returns a Matrix

The effort for overriding e.g. operator*() for all derived classes is increased by the fact that there are derived classes for 3- and 2-dimensional vectors, etc.

I understand that a solution is to define a cast from Matrix to Vector (and to Vector3, Vector2, ...) but this would involve copying all entries (which are, for efficiency, stack arrays).

Is there a more efficient solution? And, if not, would it generally be considered cleaner/better to

  1. duplicate all the relevant code in each derived class, or to
  2. define a cast?

In my current understanding, the conflicting problems are:

  1. Duplicate code makes the solution error-prone and more difficult to refactor.
  2. Reusing existing code requires lots of copy operations every time the "scope" changes between Matrix, Vector, Vector3, ... . Would be inefficient if used in large calculations.

Any suggestion would be most appreciated. Thanks!

like image 217
user7418923 Avatar asked Jan 14 '17 16:01

user7418923


People also ask

What does derived does not inherit from base class?

Following are the properties which a derived class doesn't inherit from its parent class : 1) The base class's constructors and destructor. 2) The base class's friend functions. 3) Overloaded operators of the base class.

Can be inherited by a derived class from a base class?

The derived class inherits all members and member functions of a base class. The derived class can have more functionality with respect to the Base class and can easily access the Base class. A Derived class is also called a child class or subclass.

What can be inherited by a derived class from a base class Mcq?

Which among the following is inherited by a derived class from base class? Explanation: The class inheriting another class, inherits all the data members and member functions that are not private. This is done to ensure the security features with maximum flexibility.

Can a derived class inherit from more than one base class?

You can derive a class from any number of base classes. Deriving a class from more than one direct base class is called multiple inheritance.


1 Answers

Yes, but only with free functions (including most operators).

template<class X, class Y,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<X>>{},int> =0,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<Y>>{},int> =0
>
friend X& operator+=(X&x, Y&& rhs)
{
  x.value += rhs.value;
  return x.
}
template<class X, class Y,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<X>>{},int> =0,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<Y>>{},int> =0
>
friend std::decay_t<X> operator+(X&&x, Y&& rhs) {
  auto r=std::forward<X>(x);
  r+=std::forward<Y>(rhs);
  return r;
}

Now if I did that right,

(d1+d2).timesTwo();

works.

I also implemented + in terms of += because that usually works well.

The fancy enable if exists because koenig lookup with very generic template operators causes strange things to happen when you pass Base and types derived from Base to template types and proceed to use + on the resulting type. By saying "only things derived from Base", the right thing happens.

We need to use a template free friend function so we can get the type of "*this" (as it where) within the template to change our return type. This cannot be done in a template member function.

The enable_if clause does not work well in MSVC, but is best practice in other compilers. For MSVC use class=enable_if instead of enable_if=0. The reason why the =0 is best is out of scope here.

like image 91
Yakk - Adam Nevraumont Avatar answered Sep 25 '22 10:09

Yakk - Adam Nevraumont