我正在编写一些代码,需要经常在同类坐标和笛卡尔坐标之间进行切换:
(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()) {}
^~~
“不匹配任何模板声明”似乎是一个明显的线索,但我不确定为什么其他已经存在的行仍然有效,因为它们似乎没有声明任何额外的内容?
我希望有人可以提供帮助:
让我知道是否需要提供其他代码片段,也许那里有些东西看起来并不相关,但确实有用。
答案 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");
致谢。