I am curious to know why root doesn’t make use of covariant return types in the MathCore/More libraries.
This causes a small issue when implementing a class that depends on an object derived from the ROOT::Math::IParametricFunctionMultiDim abstract base class. This was discovered because I need to store a smart pointer to a ROOT::Math::IParametricFunctionMultiDim type. It MUST be derived from this base class and not ROOT::Math::IBaseFunctionMultiDim because my class requires the function to be parametrized. However, when I tried to Clone my function to initialize my smart_ptr like this…
struct A
{
private:
boost::shared_ptr<ROOT::Math::IParametricFunctionMultiDim> fFunc;
public:
A( const ROOT::Math::IParametricFunctionMultiDim& func) : fFunc(func.Clone()) { /* ... */}
/* the rest of the class */
};
This generates error messages like such…
/usr/include/boost/smart_ptr/shared_ptr.hpp:187:50: error: invalid conversion from ‘ROOT::Math::IBaseFunctionMultiDim*’ to ‘ROOT::Math::IParametricFunctionMultiDim*’ [-fpermissive]
This is simply b/c the smart_ptr believes me when I tell it that I am giving it the more derived type and makes no attempt to upcast it (it is just a template). I can write a bandaid by upcasting it myself but there really isn’t a reason to do so because C++ supports the ability of virtual functions to return objects of more derived types.
To improve this, I can add the pure virtual Clone method that returns a ROOT::Math::IParametricFunctionMultiDim pointer to its class, which I think makes sense b/c some people will want to treat this as the base class rather than the IBaseFunctionMultiDim (like me ).
namespace ROOT {
namespace Math {
class IParametricFunctionMultiDim
{
/*
* current class definition
*/
public:
virtual IParametricFunctionMultiDim* Clone() const = 0;
};
} // end namespace Math
} // end namespace ROOT
This makes my code compile, however, it causes some of the other more derived classes of the MathMore libraries, such as
ROOT::Math::MultiDimParamGradFunctionAdapter, to complain.
include/Math/MultiDimParamFunctionAdapter.h:115:23: error: invalid covariant return type for ‘virtual ROOT::Math::MultiDimParamFunctionAdapter::BaseFunc* ROOT::Math::MultiDimParamFunctionAdapter::Clone() const’
include/Math/IParamFunction.h:115:41: error: overriding ‘virtual ROOT::Math::IParametricFunctionMultiDim* ROOT::Math::IParametricFunctionMultiDim::Clone() const’
which occurs b/c these classes’ virtual Clone methods return pointers to one of the two ultimate base classes, ROOT::Math::IBaseFunctionMultiDim, rather than the ROOT::Math::IParametricFunctionMultiDim (or better yet themselves). The meaning of returning this type is nonsensical b/c they inherit from two abstract base classes rather than just one as the code implies.
Is it possible to make all of the mathmore/mathcore (and the rest of ROOT if applicable), do one of two things
-
Utilize covariant return types by making all classes return a pointer of their own type from their inherited Clone method themselves. I think this is an optimal improvement from an understandability point of view and should be transparent to implement. There is a nice article about this topic here http://www.lwithers.me.uk/articles/covariant.html.
-
If item 1 is not implemented, then make the more derived objects of ROOT::Math::IParametricFunctionMultiDim to return pointers to ROOT::Math::IParametricFunctionMultiDim rather than ROOT::Math::IBaseFunctionMultiDim from their Clone methods.
Thanks,
Jeromy