Могу ли я привести / преобразовать шаблоны в другие типы шаблонов? - PullRequest
0 голосов
/ 03 марта 2019

Я пишу код, в котором мне нужно будет часто переключаться между однородными и декартовыми координатами:

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

Я использую библиотеку, в которой уже есть шаблоны для вектора произвольного размера и типа, а такжекак typedefs для нескольких распространенных, например, так:

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;

Что бы вывести следующее (векторные шаблоны перегружены << операторы для печати): </p>

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) {};

Я не совсем понимаю этот макет (часть шаблона <> template <>), но очевидно, что это позволяет приводить между векторами чисел типа int / float, и я подтвердилэтот.Однако, когда я пытался сделать то же самое, я получаю ошибки:

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()) {}
     ^~~

"Не соответствует ни одному объявлению шаблона"кажется очевидной подсказкой, но я не уверен, почему другие строки, которые уже были там, работают, поскольку они, кажется, не объявляют ничего лишнего?

Я надеюсь, что кто-то может помочь:

  • Объясните код (template <> template <>), который, кажется, позволяет мне приводить между векторами float / int (который уже присутствовал в библиотеке)
  • Объясните, почему код, который я добавилне работает, когда на поверхности это кажется очень похожим

Дайте мне знать, если мне нужно предоставить какие-либо другие фрагменты кода, возможно, там есть что-то, что не выглядит актуальным, но есть.

1 Ответ

0 голосов
/ 03 марта 2019

Вы можете создать подкласс.

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");

С уважением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...