-1

I want to make a common general template which specifies all 3D math on 3D vectors and than specialize it for float (double) (called Vec3d ) and integers (called Vec3i). I want to do this without any reimplementation of the common code.

Somebody recommanded to do it by inheritance instead of specialization. But when I do I get this error:

 main.cpp:34:12: error: could not convert ‘dR.Vec3d::<anonymous>.Vec3TYPE<TYPE>::operator*<double>(k)’ from ‘Vec3TYPE<double>’ to ‘Vec3d’

return dR * k; // there is the error

Code is like this (extracted the relevant part):

#include <math.h>
#include <cstdio>

// definition of general template
template <class TYPE> 
class Vec3TYPE{
    public:
    union{
        struct{ TYPE x,y,z; };
        struct{ TYPE a,b,c; };
        TYPE array[3];
    };
    inline void set( TYPE f                        ) { x=f;   y=f;   z=f;   };
    inline void set( TYPE fx, TYPE fy, TYPE fz     ) { x=fx;  y=fy;  z=fz;  };
    inline void set( const Vec3TYPE& v             ) { x=v.x; y=v.y; z=v.z; };
    inline Vec3TYPE operator+ ( TYPE f   ) const { Vec3TYPE vo; vo.x=x+f; vo.y=y+f; vo.z=z+f; return vo; };
    inline Vec3TYPE operator* ( TYPE f   ) const { Vec3TYPE vo; vo.x=x*f; vo.y=y*f; vo.z=z*f; return vo; };
    inline Vec3TYPE operator+ ( const Vec3TYPE& vi ) const { Vec3TYPE vo; vo.x=x+vi.x; vo.y=y+vi.y; vo.z=z+vi.z; return vo; };
    inline Vec3TYPE operator- ( const Vec3TYPE& vi ) const { Vec3TYPE vo; vo.x=x-vi.x; vo.y=y-vi.y; vo.z=z-vi.z; return vo; };
    inline Vec3TYPE operator* ( const Vec3TYPE& vi ) const { Vec3TYPE vo; vo.x=x*vi.x; vo.y=y*vi.y; vo.z=z*vi.z; return vo; };
    inline Vec3TYPE operator/ ( const Vec3TYPE& vi ) const { Vec3TYPE vo; vo.x=x/vi.x; vo.y=y/vi.y; vo.z=z/vi.z; return vo; };
};

// specialization
class Vec3i : public Vec3TYPE<int>{};   // int   version
class Vec3d : public Vec3TYPE<double>{  // float version
    public:
    inline double norm ( ) const { return  sqrt( x*x + y*y + z*z ); };
};

inline Vec3d getForce( Vec3d dR, double k ){
    return dR * k; // there is the error
}


// main
int main(){
    Vec3d a; a.set( 1.0, 2.0, 3.0 );

    // this works
    Vec3d b; b.set( a * 4.0  );
    printf( " %f %f %f \n", b.x, b.y, b.z );

    // this does not
    Vec3d b_; b_.set( getForce( a, 4.0 )  );
    printf( " %f %f %f \n", b.x, b.y, b.z );
}
Prokop Hapala
  • 2,424
  • 2
  • 30
  • 59
  • 1
    There seems to be some extra closing braces and some missing semicolons, inconsistent indentation, as well as some extra unnecessary semicolons. Please copy-paste the actual code instead of rewriting in the text box. Or even better, please create a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve) and show us. – Some programmer dude Jan 19 '15 at 15:32
  • as stated, you are missing missing a ; at the end of class Vec3Type { };, and methods don't need the ;. But, supposing you were interested in math vectors and not syntax, have a look at http://glm.g-truc.net – Exceptyon Jan 19 '15 at 15:48
  • What's the point of inheritance here? How is `Vec3d` extending its base `Vec3TYPE`? In your snippet, inheritance solves no problems and hence makes no sense at all. And yes, it causes the problem you're having. – Walter Jan 19 '15 at 15:54
  • OK, sorry. I now made the "minimal working example" as recommanded – Prokop Hapala Jan 19 '15 at 15:55
  • You might want to read about the [Curiously recurring template pattern](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). – Some programmer dude Jan 19 '15 at 16:01
  • Why not use an existing library implementation that has been debugged already? Why specialise and not just support norm for all types? – Neil Kirk Jan 19 '15 at 16:03
  • I used inheritance just to specialize type without reimpletation of the methods. According what people advice here http://stackoverflow.com/questions/25486033/c-class-template-specialization-without-having-to-reimplement-everything – Prokop Hapala Jan 19 '15 at 16:04
  • Why to specialize? Well 1) I just want to write Vec3d insteda of Vec3TYPE .. because it is shorter. 2) It is just example - some methods cannot be well defined for integer case. – Prokop Hapala Jan 19 '15 at 16:06

4 Answers4

1

Using the Curiously recurring template pattern I mentioned in a comment, you could maybe do something like.

template <typename TYPE, typename CHILD>
class Vec3TYPE{
    public:
    union{
        struct{ TYPE x,y,z; };
        struct{ TYPE a,b,c; };
        TYPE array[3];
    };

    inline CHILD operator+ ( TYPE f   ) const { CHILD vo; vo.x=x+f; vo.y=y+f; vo.z=z+f; return vo; };
    // etc.
};

// specialization
class Vec3i : public Vec3TYPE<int, Vec3i>{};   // int   version
class Vec3d : public Vec3TYPE<double, Vec3d>{  // float version
    public:
    inline double norm ( ) const { return  sqrt( x*x + y*y + z*z ); };
};
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

The problem is that Vec3TYPE<TYPE>::operator* returns an object of type Vec3TYPE<TYPE>. getForce on the other hand must return a Vec3d. A simple workaround to your design could be to make Vec3d constructible from Vec3TYPE<double> by adding following constructor which copy constructs the base object from the parameter.

Vec3d(const Vec3TYPE<double>& parent): Vec3TYPE<double>(parent){}

However, I'm not convinced that the inheritance adds anything useful. I suggest that you consider a design where you use Vec3TYPE without derivatives. Calculating norm is useful for integer vectors as well. Obviously, returning the norm as an integer is usually not what you'd usually want. You can fix that by making it a template:

template<class Rtype = TYPE> // the default param requires c++11
inline RType norm ( ) const { return  sqrt( x*x + y*y + z*z ); };
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1) this copy construct cost some performace (?) 2) If I specify constructor I cannot initialize the varibales by list like getForce( {1.0,2.0,3.0}, 10 ) – Prokop Hapala Jan 19 '15 at 16:28
  • 1) Possibly. The compiler might elide the copy by writing the result of `operator*` directly as the sub-object of `Vec3d`, but you'd have to test to be sure. The suggestion is primarily just a quick band-aid that doesn't change the design. A better design is to use CRTP such as Joachims answer does or possibly getting rid of inheritance entirely like I suggest in mine 2) You can still do that if you also define a constructor that accepts three `TYPE` values, but only in c++11 or later. – eerorika Jan 19 '15 at 16:47
  • Actually, almost only reason why I want do this derived (specialized) types right now is that I don't want to write `Vec3TYPE` everywhere ... `Vec3d` is just shorter alias. Originaly I used `using Vec3d = Vec3TYPE;` instead of inheritance, but I had also some issues with that (don't remember what was it). – Prokop Hapala Jan 19 '15 at 17:05
0

The error message is horrible, but the cause is that there is no conversion from Vec3TYPE<double> to Vec3d.

(dR.Vec3d::<anonymous>.Vec3TYPE<TYPE>::operator*<double>(k) refers to the result of the operation dR * k, which isn't the most obvious thing in the world.)

The reason that there is no conversion is that there is no default conversion from base class to subclass.

The reason that the conversion is needed is that operator* returns a Vec3TYPE<double>, not a Vec3d.

If you really want to use inheritance, you'll need to provide a constructor that takes a Vec3TYPE<double> in Vec3d.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • Hi, yes, I understand why I get this error. I just don't know how to overcome it. If it is possibe to do this type specialization other than by inheritance, I would do it. I just don't want to rewrite implementation of vector math for each specific type. – Prokop Hapala Jan 19 '15 at 16:01
0

The problem, as explained in the other answers, is caused by using inheritance without a good reason. Simply don't use inheritance here. Moreover, for general functionality I would use non-member functions, e.g. (declared in the same namespace as Vec3TYPE<>)

inline double norm(Vec3TYPE<double> const&v) noexcept
{
  return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
}

Btw, why should Vec3TYPE<int> not also have a norm (returning int)?


Without inheritance, you simply define

typedef Vec3TYPE<double> Vec3d;
typedef Vec3TYPE<int> Vec3i;
Walter
  • 44,150
  • 20
  • 113
  • 196
  • OK I just want to make type Vec3d and Vec3i without reimplementing all functionality several times. 2) Because the result is not an integer. ( OK this is just example, originaly I have normalize() here ) – Prokop Hapala Jan 19 '15 at 16:13