Pergunta

I've got the following:

template<typename T> class CVector3
{
    CVector3<T> &normalize();
    // more stuff
};

typedef CVector3<float> Vector3f;
typedef CVector3<double> Vector3d;

I basically want to add a method, toPoint(), that returns a struct Point3f if T=float and a struct Point3d if T=double. I tried replacing the two typedefs with:

class Vector3f: public CVector3<float>
{
    Point3f toPoint() const;
};

class Vector3d: public CVector3<double>
{
    Point3d toPoint() const;
};

This does not work, however, because now normalize() is broken: It no longer returns a Vector3f, but a CVector3<float>, which is incompatible with Vector3f, because it's, in fact, the base class. I could add wrapper methods for normalize() and any other public method in the base class, but I don't want to do this, because it would make maintaining these classes tedious.

I also tried putting the typedefs back in and adding outside the template definition:

template<>
Point3f CVector3<float>::toPoint() const;

template<>
Point3d CVector3<double>::toPoint() const;

This doesn't compile, because toPoint() is not declared inside the template definition. I can't put it inside, because of the return type Point3f/Point3d.

How do I do this? Any help is greatly appreciated!

Foi útil?

Solução 3

Thanks to your replies, I have now figured out a way that should work:

template<typename T, typename P> class CVector3
{
    CVector3<T, P> &normalize();
    // more stuff

    P toPoint() const;
};

typedef CVector3<float, Point3f> Vector3f;
typedef CVector3<double, Point3d> Vector3d;

I'm going to try this and tell you if it worked later. Cheers!

EDIT: Yup, it worked! I had to define toPoint() like this:

template<>
Point3f CVector3<float, Point3f>::toPoint() const
{
    Point3f pt = { x, y, z };
    return pt;
}

Your answers with traits are certainly a more general solution, but since Point3f is the natural pendant for Vector3f, I like a second template parameter better.

Outras dicas

You could use a traits style helper class.

template<typename T> CVectorTraits {};
template<> CVectorTraits<double> { typedef Point3d PointType; }
template<> CVectorTraits<float> { typedef Point3f PointType; }

template<typename T> class CVector3
{
    CVector3<T> &normalize();
    // more stuff
    typename CVectorTraits<T>::PointType toPoint() const;
};

You can use type traits:

template<typename T>
struct VectorTraits;

template<>
struct VectorTraits<float> {
     typedef Point3f Point;
};
template<>
struct VectorTraits<double> {
     typedef Point3d Point;
};

template<typename T> class CVector3
{
    CVector3<T> &normalize();

    typename VectorTraits<T>::Point 
    toPoint() const;

    // more stuff
};

typedef CVector3<float> Vector3f;
typedef CVector3<double> Vector3d;

You can improve syntax for clients, and enforce the constraint to ensure clients don't get the template arguments wrong by using a specialization.

struct Point3f { float x, y, z; };
struct Point3d { double x, y, z; };

// Base template toPoint returns Point3f.
template<typename T, typename U = Point3f>
class Vector3
{
public:
   Vector3& normalize(){ return Vector3(); }
   U toPoint(){ return Point3f(); }
};

// Specialization for double, toPoint returns Point3d.
template<>
class Vector3<double>
{
public:
   Vector3& normalize(){ return Vector3(); }
   Point3d toPoint(){ return Point3d(); }
};


TEST(TemplateTests2, Test3)
{
   Vector3<float> v1;
   Point3f p1 = v1.toPoint();

   Vector3<double> v2;
   Point3d p2 = v2.toPoint();
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top