我可以将模板转换/转换为其他模板类型吗?

时间:2019-03-03 00:57:37

标签: c++ templates

我正在编写一些代码,需要经常在同类坐标和笛卡尔坐标之间进行切换:

(x, y, z) -> (x, y, z, w = 1)
(x, y, z, w) -> (x/w, y/w, z/w)

我正在使用一个库,该库已经具有用于任意大小和类型的矢量的模板,以及用于一些常见的typedef的模板,例如:

typedef vec<3,  float> Vec3f;
typedef vec<4,  float> Vec4f;

我很想知道是否可以添加一种方法,因此我可以使用上面显示的均匀<->笛卡尔关系轻松地在两者之间切换。我想要这样的东西:

Vec3f cart1(10, 15, 20);
Vec4f homo1;
homo1 = (Vec4f) cart1;
std::cout << "homo1: " << homo1 << std::endl;

Vec4f homo2(10, 15, 20, 5);
Vec3f cart2;
cart2 = (Vec3f) homo2;
std::cout << "cart2: " << cart2 << std::endl;

将输出以下内容(矢量模板已重载<<用于打印的运算符):

homo1: 10, 15, 20, 1
cart2: 2, 3, 4

这是标题中的相关模板代码,请注意,我添加了vec <4,T>部分:

#include <cmath>
#include <vector>
#include <cassert>
#include <iostream>

template<size_t DimCols,size_t DimRows,typename T> class mat;

template <size_t DIM, typename T> struct vec {
    vec() { for (size_t i=DIM; i--; data_[i] = T()); }
          T& operator[](const size_t i)       { assert(i<DIM); return data_[i]; }
    const T& operator[](const size_t i) const { assert(i<DIM); return data_[i]; }
private:
    T data_[DIM];
};

/////////////////////////////////////////////////////////////////////////////////

template <typename T> struct vec<3,T> {
    vec() : x(T()), y(T()), z(T()) {}
    vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
    template <class U> vec<3,T>(const vec<3,U> &v);
          T& operator[](const size_t i)       { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
    const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
    float norm() { return std::sqrt(x*x+y*y+z*z); }
    vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; }

    T x,y,z;
};

/////////////////////////////////////////////////////////////////////////////////

// my Vec4f template
template <typename T> struct vec<4,T> {
    vec() : x(T()), y(T()), z(T()), w(T()) {}
    vec(T X, T Y, T Z) : x(X), y(Y), z(Z), w(1.f) {}
    template <class U> vec<4,T>(const vec<4,U> &v);
          T& operator[](const size_t i)       { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); }
    const T& operator[](const size_t i) const { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); }

    T x,y,z,w;
};

typedef vec<3,  float> Vec3f;
typedef vec<4,  float> Vec4f;

现在,匹配的.cpp文件具有已经可以执行此操作的代码,而不仅仅是我想要的类型:

template <> template <> vec<3,int>  ::vec(const vec<3,float> &v) : x(int(v.x+.5f)),y(int(v.y+.5f)),z(int(v.z+.5f)) {};
template <> template <> vec<3,float>::vec(const vec<3,int> &v)   : x(v.x),y(v.y),z(v.z) {};

我不太了解这种布局(模板<>模板<>部分),但是很明显,这允许在int / floats向量之间进行转换,并且我已经证实了这一点。但是,当我尝试执行相同操作时,出现错误:

template <> template <> vec<3,float>::vec(const vec<4,float> &v) : x(v.x / v.w),y(v.y / v.w),z(v.z / v.w) {};
template <> template <> vec<4,float>::vec(const vec<3,float> &v) : x(v.x),y(v.y),z(v.z),w(1.f) {};

使用上述代码进行编译即可得到

tinyrenderer-files/geometry.cpp:9:25: error: template-id ‘vec<>’ for ‘vec<3, float>::vec(const vec<4, float>&)’ does not match any template declaration
 template <> template <> vec<3,float>::vec(const vec<4,float> &v) : x(v.x / v.w),y(v.y / v.w),z(v.z / v.w) {};
                         ^~~~~~~~~~~~
In file included from tinyrenderer-files/geometry.cpp:1:0:
tinyrenderer-files/geometry.h:32:30: note: candidates are: constexpr vec<3, float>::vec(vec<3, float>&&)
 template <typename T> struct vec<3,T> {
                              ^~~~~~~~
tinyrenderer-files/geometry.h:32:30: note:                 constexpr vec<3, float>::vec(const vec<3, float>&)
tinyrenderer-files/geometry.h:35:24: note:                 template<class U> vec<3, T>::vec(const vec<3, U>&) [with U = U; T = float]
     template <class U> vec<3,T>(const vec<3,U> &v);
                        ^~~~~~~~
tinyrenderer-files/geometry.h:34:5: note:                 vec<3, T>::vec(T, T, T) [with T = float]
     vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
     ^~~
tinyrenderer-files/geometry.h:33:5: note:                 vec<3, T>::vec() [with T = float]
     vec() : x(T()), y(T()), z(T()) {}
     ^~~
tinyrenderer-files/geometry.cpp:10:25: error: template-id ‘vec<>’ for ‘vec<4, float>::vec(const vec<3, float>&)’ does not match any template declaration
 template <> template <> vec<4,float>::vec(const vec<3,float> &v) : x(v.x),y(v.y),z(v.z),w(1.f) {};
                         ^~~~~~~~~~~~
In file included from tinyrenderer-files/geometry.cpp:1:0:
tinyrenderer-files/geometry.h:47:30: note: candidates are: constexpr vec<4, float>::vec(vec<4, float>&&)
 template <typename T> struct vec<4,T> {
                              ^~~~~~~~
tinyrenderer-files/geometry.h:47:30: note:                 constexpr vec<4, float>::vec(const vec<4, float>&)
tinyrenderer-files/geometry.h:50:24: note:                 template<class U> vec<4, T>::vec(const vec<4, U>&) [with U = U; T = float]
     template <class U> vec<4,T>(const vec<4,U> &v);
                        ^~~~~~~~
tinyrenderer-files/geometry.h:49:5: note:                 vec<4, T>::vec(T, T, T) [with T = float]
     vec(T X, T Y, T Z) : x(X), y(Y), z(Z), w(1.f) {}
     ^~~
tinyrenderer-files/geometry.h:48:5: note:                 vec<4, T>::vec() [with T = float]
     vec() : x(T()), y(T()), z(T()), w(T()) {}
     ^~~

“不匹配任何模板声明”似乎是一个明显的线索,但我不确定为什么其他已经存在的行仍然有效,因为它们似乎没有声明任何额外的内容?

我希望有人可以提供帮助:

  • 解释似乎让我在库中已经存在的float / int向量之间进行转换的代码(模板<>模板<>)
  • 解释为什么我添加的代码看上去很相似时不起作用

让我知道是否需要提供其他代码片段,也许那里有些东西看起来并不相关,但确实有用。

1 个答案:

答案 0 :(得分:1)

您可以继承。

class Homogeneous;
class Cartesian : public Vec3f
{
    Cartesian () : Vec3f() {}
    Cartesian (float a, float b, float c) : Vec3f(a, b, c, d) {}
    explicit Cartesian (const Homogeneous& v); // Define later, we don't know Homogeneous yet
}

class Homogeneous : public Vec4f
{
public:
    Homogeneous() : Vec4f() {}
    Homogeneous(float a, float b, float c, float d) : Vec4f(a, b, c, d) {}
    explicit Homogeneous(const Cartesian& v) : Vec4f() { // do conversion }
}

Cartesian::Cartesian (const Homogeneous& v) : Vec3f() { // do conversion }

类似的东西。与使用Vec3f相比,它还具有更多的类型安全性。

如果您在没有虚拟析构函数的情况下使用公共继承,则应该执行类似的操作

static_assert(sizeof(Cartesian) == sizeof(Vec3f), "Don't");
static_assert(sizeof(Homogeneous) == sizeof(Vec4f), "Don't");

致谢。