C++ 如何以名称和数组形式访问成员?

C++ 如何以名称和数组形式访问成员?,c++,templates,C++,Templates,我有很多向量类。我有一个2D点 VEC2YT,一个3D点 VEC3YT和4D点 VEC4YT(当你有图形的时候,你经常想要这些,这是图形代码,但是问题有一个通用的C++味道)。 现在,我已经声明了两个成员x和yvec3_t子类vec2_t,并具有第三个成员zvec4\u t子类vec3\u t并添加w成员 我有很多近似重复的代码用于运算符重载计算,比如距离、叉积、矩阵乘法等等 我曾经有过一些错误,当我没有为子类显式声明操作符时,情况就是这样。复制品让我很烦 此外,我还希望以数组的形式访问这些成员

我有很多向量类。我有一个2D点<代码> VEC2YT,一个3D点<代码> VEC3YT和4D点<代码> VEC4YT(当你有图形的时候,你经常想要这些,这是图形代码,但是问题有一个通用的C++味道)。 现在,我已经声明了两个成员
x
y
vec3_t
子类
vec2_t
,并具有第三个成员
z
vec4\u t
子类
vec3\u t
并添加
w
成员

我有很多近似重复的代码用于运算符重载计算,比如距离、叉积、矩阵乘法等等

我曾经有过一些错误,当我没有为子类显式声明操作符时,情况就是这样。复制品让我很烦

此外,我还希望以数组的形式访问这些成员;这对于某些具有数组参数的OpenGL函数非常有用

我想,也许有了
vec\t
模板,我就可以不用子类化就创建向量类了。然而,这带来了两个问题:

  • 如何拥有数量可变且也是数组项的成员,并确保它们对齐?我不想失去我指定的成员
    vec.x
    vec.d[0]
    或其他imo产品好得多,如果可能的话,我想保留它
  • 当您采用模板化路线时,如何在CPP源文件中而不是在头文件中有许多更昂贵的方法

  • 一种方法是:

    struct vec_t {
       float data[3];
       float& x;
       float& y;
       float& z;
       vec_t(): x(data[0]), y(data[1]), z(data[2]) {}
    };
    

    在这里,它正确地用名称给数组成员加上别名,但是我测试过的编译器(GCC)似乎没有发现它们只是别名,因此类的大小相当大(对于我可能有一个数组,并且希望传递的东西,例如作为VBO;所以大小是个大问题)您将如何对其进行模板参数化,以便只有
    vec4\t
    具有
    w
    成员?

    这是一种方法:

    #include<cstdio>
    
    class vec2_t{
    public:
        float x, y;
        float& operator[](int idx){ return *(&x + idx); }
    };
    
    class vec3_t : public vec2_t{
    public:
        float z;
    };
    
    编辑#2:我有另一种选择,可以只编写一次运算符,而不会得到更大的类,如:

    #include <cstdio>
    #include <cmath>
    
    template<int k>
    struct vec{
    
    };
    
    template<int k>
    float abs(vec<k> const&v){
        float a = 0;
        for (int i=0;i<k;i++)
            a += v[i]*v[i];
        return sqrt(a);
    }
    
    template<int u>
    vec<u> operator+(vec<u> const&a, vec<u> const&b){
        vec<u> result = a;
        result += b;
        return result;
    }
    
    template<int u>
    vec<u>& operator+=(vec<u> &a, vec<u> const&b){
        for (int i=0;i<u;i++)
            a[i] = a[i] + b[i];
        return a;
    }
    
    template<int u>
    vec<u> operator-(vec<u> const&a, vec<u> const&b){
        vec<u> result;
        for (int i=0;i<u;i++)
            result[i] = a[i] - b[i];
        return result;
    }
    
    template<>
    struct vec<2>{
        float x;
        float y;
        vec(float x=0, float y=0):x(x), y(y){}
        float& operator[](int idx){
            return idx?y:x;
        }
        float operator[](int idx) const{
            return idx?y:x;
        }
    };
    
    template<>
    struct vec<3>{
        float x;
        float y;
        float z;
    
        vec(float x=0, float y=0,float z=0):x(x), y(y),z(z){}
        float& operator[](int idx){
            return (idx==2)?z:(idx==1)?y:x;
        }
        float operator[](int idx) const{
            return (idx==2)?z:(idx==1)?y:x;
        }
    };
    
    #包括
    #包括
    模板
    结构向量{
    };
    模板
    浮动防抱死制动系统(矢量恒定和可变){
    浮点数a=0;
    对于(inti=0;i一个可能的解决方案(我认为)

    main.cpp:

    #include <iostream>
    #include "extern.h"
    
    template <int S>
    struct vec_t_impl
    {
        int values[S];
        bool operator>(const vec_t_impl<S>& a_v) const
        {
            return array_greater_than(values, a_v.values, S);
        }
        void print() { print_array(values, S); }
    
        virtual ~vec_t_impl() {}
    };
    
    struct vec_t2 : vec_t_impl<2>
    {
        vec_t2() : x(values[0]), y(values[1]) {}
        int& x;
        int& y;
    };
    
    struct vec_t3 : vec_t_impl<3>
    {
        vec_t3() : x(values[0]), y(values[1]), z(values[2]) {}
        int& x;
        int& y;
        int& z;
    };
    
    int main(int a_argc, char** a_argv)
    {
        vec_t3 a;
        a.x = 5;
        a.y = 7;
        a.z = 20;
    
        vec_t3 b;
        b.x = 5;
        b.y = 7;
        b.z = 15;
    
        a.print();
        b.print();
    
        cout << (a > b) << "\n";
    
        return 0;
    }
    
    extern.cpp:

    #include <iostream>
    
    bool array_greater_than(const int* a1, const int* a2, const size_t size)
    {
        for (size_t i = 0; i < size; i++)
        {
            if (*(a1 + i) > *(a2 + i))
            {
                return true;
            }
        }
        return false;
    }
    
    void print_array(const int* a1, const size_t size)
    {
        for (size_t i = 0; i < size; i++)
        {
            if (i > 0) cout << ", ";
            std::cout << *(a1 + i);
        }
        std::cout << '\n';
    }
    

    在这里,一个简单的解决方案可能是最好的:

    struct Type
    {
        enum { x, y };
        int values[2];
    };
    
    Type t;
    if (t.values[0] == t.values[Type::x])
        cout << "Good";
    

    注意:对代码进行了大量更新和改进

    以下代码使用宏来保持代码干净,并使用部分专用化来提供成员。它严重依赖继承性,但这使得它很容易扩展到任意维度。它还尽可能通用,这就是为什么基础类型是模板参数:

    // forward declaration, needed for the partial specializations
    template<unsigned, class> class vec;
    
    namespace vec_detail{
    // actual implementation of the member functions and by_name type
    // partial specializations do all the dirty work
    template<class Underlying, unsigned Dim, unsigned ActualDim = Dim>
    struct by_name_impl;
    
    // ultimate base for convenience
    // this allows the macro to work generically
    template<class Underlying, unsigned Dim>
    struct by_name_impl<Underlying, 0, Dim>
    { struct by_name_type{}; };
    
    // clean code after the macro
    // only need to change this if the implementation changes
    #define GENERATE_BY_NAME(MEMBER, CUR_DIM) \
        template<class Underlying, unsigned Dim> \
        struct by_name_impl<Underlying, CUR_DIM, Dim> \
            : public by_name_impl<Underlying, CUR_DIM - 1, Dim> \
        { \
        private: \
            typedef vec<Dim, Underlying> vec_type; \
            typedef vec_type& vec_ref; \
            typedef vec_type const& vec_cref; \
            typedef by_name_impl<Underlying, CUR_DIM - 1, Dim> base; \
        protected: \
            struct by_name_type : base::by_name_type { Underlying MEMBER; }; \
            \
        public: \
            Underlying& MEMBER(){ \
                return static_cast<vec_ref>(*this).member.by_name.MEMBER; \
            } \
            Underlying const& MEMBER() const{ \
                return static_cast<vec_cref>(*this).member.by_name.MEMBER; \
            } \
        }
    
    GENERATE_BY_NAME(x, 1);
    GENERATE_BY_NAME(y, 2);
    GENERATE_BY_NAME(z, 3);
    GENERATE_BY_NAME(w, 4);
    
    // we don't want no pollution
    #undef GENERATE_BY_NAME
    } // vec_detail::
    
    template<unsigned Dim, class Underlying = int>
    class vec
        : public vec_detail::by_name_impl<Underlying, Dim>
    {
    public:
        typedef Underlying underlying_type;
    
        underlying_type& operator[](int idx){
            return member.as_array[idx];
        }
    
        underlying_type const& operator[](int idx) const{
            return member.as_array[idx];
        }
    
    private:
        typedef vec_detail::by_name_impl<Underlying, Dim> base;
        friend struct vec_detail::by_name_impl<Underlying, Dim>;
        typedef typename base::by_name_type by_name_type;
    
        union{
            by_name_type by_name;
            underlying_type as_array[Dim];
        } member;
    };
    
    //转发声明,部分专门化所需
    模板类vec;
    名称空间向量详细信息{
    //成员函数和按名称类型的实际实现
    //部分专业化完成了所有肮脏的工作
    模板
    按名称的结构\u impl;
    //方便的终极基础
    //这使宏可以正常工作
    模板
    按名称的结构\u impl
    {struct by_name_type{};};
    //清除宏后的代码
    //只有在实现更改时才需要更改此选项
    #定义按名称生成(成员、当前维度)\
    模板\
    按名称的结构\u impl\
    :public by_name_impl\
    { \
    私人:\
    类型定义向量向量类型\
    类型定义向量类型和向量引用\
    类型定义向量类型常量和向量cref\
    typedef by_name_impl base\
    受保护:\
    struct by_name_type:base::by_name_type{基础成员;}\
    \
    公众:\
    基础成员({\
    返回静态_cast(*this).member.by_name.member\
    } \
    基础常量&MEMBER()常量{\
    返回静态_cast(*this).member.by_name.member\
    } \
    }
    通过_名称(x,1)生成_;
    通过_名称生成_(y,2);
    通过_名称生成_(z,3);
    通过_名称生成_(w,4);
    //我们不希望没有污染
    #未定义按\u名称生成\u
    }//车辆详细信息::
    模板
    vec类
    :public vec_detail::by_name_impl
    {
    公众:
    typedef底层_类型;
    基础类型和运算符[](int-idx){
    返回成员.as_数组[idx];
    }
    基础_类型常量和运算符[](int-idx)常量{
    返回成员.as_数组[idx];
    }
    私人:
    typedef vec_detail::by_name_impl base;
    friend struct vec_detail::by_name_impl;
    typedef typename base::by_name_type by_name_type;
    联合{
    按名称键入按名称;
    底层_类型为_数组[Dim];
    }成员;
    };
    
    用法:

    #include <iostream>
    
    int main(){
        typedef vec<4, int> vec4i;
        // If this assert triggers, switch to a better compiler
        static_assert(sizeof(vec4i) == sizeof(int) * 4, "Crappy compiler!");
        vec4i f;
        f.w() = 5;
        std::cout << f[3] << '\n';
    }
    
    #包括
    int main(){
    typedef-vec-vec4i;
    //如果此断言触发,请切换到更好的编译器
    静态断言(sizeof(vec4i)==sizeof(int)*4,“蹩脚的编译器!”);
    vec4i-f;
    f、 w()=5;
    
    std::cout如果您不想自己编写,可以查看以下建议的一些库:

    如果使用一个特定的编译器,则可以使用非标准方法,如打包信息或无名结构(Visual Studio):

    另一方面,将几个成员变量强制转换到一个数组似乎很危险,因为编译器可能会更改类布局

    因此,逻辑解决方案似乎只有一个数组,并使用方法访问该数组。例如:

    template<size_t D>
    class  Vec
    {
    private: 
      float data[D];
    
    public:  // Constants
      static const size_t num_coords = D;
    
    public:  // Coordinate Accessors
      float& x()             { return data[0]; }
      const float& x() const { return data[0]; }
      float& y()             { static_assert(D>1, "Invalid y()"); return data[1]; }
      const float& y() const { static_assert(D>1, "Invalid y()"); return data[1]; }
      float& z()             { static_assert(D>2, "Invalid z()"); return data[2]; }
      const float& z() const { static_assert(D>2, "Invalid z()"); return data[2]; }
    
    public: // Vector accessors
      float& operator[](size_t index) {return data[index];}
      const float& operator[](size_t index) const {return data[index];}
    
    public:  // Constructor
      Vec() {
        memset(data, 0, sizeof(data));
      }
    
    public:  // Explicit conversion
      template<size_t D2>
      explicit Vec(const Vec<D2> &other) {
        memset(data, 0, sizeof(data));
        memcpy(data, other.data, std::min(D, D2));
      }
    };
    
    模板
    Vec类
    {
    私人:
    浮动数据[D];
    公共常数
    静态常数size\u t num\u coords=D;
    公共访问器
    float&x(){返回数据[0];}
    常量float&x()常量{返回数据[0];}
    float&y(){static_assert(D>1,“无效的y()”);返回数据[1];}
    常量float&y()常量{static_assert(D>1,“无效的y()”);返回数据[1];}
    float&z(){static_assert(D>2,“无效的z()”);返回数据[2];}
    常量float&z()常量{static_assert(D>2,“无效的z()”);返回数据[2];}
    公共向量存取器
    浮动和运算符[](si)
    
    struct Type
    {
        enum { x, y };
        int values[2];
    };
    
    Type t;
    if (t.values[0] == t.values[Type::x])
        cout << "Good";
    
    struct Type
    {
        int values[2];
    
        int x() const {
            return values[0];
        }
    
        void x(int i) {
            values[0] = i;
        }
    };
    
    // forward declaration, needed for the partial specializations
    template<unsigned, class> class vec;
    
    namespace vec_detail{
    // actual implementation of the member functions and by_name type
    // partial specializations do all the dirty work
    template<class Underlying, unsigned Dim, unsigned ActualDim = Dim>
    struct by_name_impl;
    
    // ultimate base for convenience
    // this allows the macro to work generically
    template<class Underlying, unsigned Dim>
    struct by_name_impl<Underlying, 0, Dim>
    { struct by_name_type{}; };
    
    // clean code after the macro
    // only need to change this if the implementation changes
    #define GENERATE_BY_NAME(MEMBER, CUR_DIM) \
        template<class Underlying, unsigned Dim> \
        struct by_name_impl<Underlying, CUR_DIM, Dim> \
            : public by_name_impl<Underlying, CUR_DIM - 1, Dim> \
        { \
        private: \
            typedef vec<Dim, Underlying> vec_type; \
            typedef vec_type& vec_ref; \
            typedef vec_type const& vec_cref; \
            typedef by_name_impl<Underlying, CUR_DIM - 1, Dim> base; \
        protected: \
            struct by_name_type : base::by_name_type { Underlying MEMBER; }; \
            \
        public: \
            Underlying& MEMBER(){ \
                return static_cast<vec_ref>(*this).member.by_name.MEMBER; \
            } \
            Underlying const& MEMBER() const{ \
                return static_cast<vec_cref>(*this).member.by_name.MEMBER; \
            } \
        }
    
    GENERATE_BY_NAME(x, 1);
    GENERATE_BY_NAME(y, 2);
    GENERATE_BY_NAME(z, 3);
    GENERATE_BY_NAME(w, 4);
    
    // we don't want no pollution
    #undef GENERATE_BY_NAME
    } // vec_detail::
    
    template<unsigned Dim, class Underlying = int>
    class vec
        : public vec_detail::by_name_impl<Underlying, Dim>
    {
    public:
        typedef Underlying underlying_type;
    
        underlying_type& operator[](int idx){
            return member.as_array[idx];
        }
    
        underlying_type const& operator[](int idx) const{
            return member.as_array[idx];
        }
    
    private:
        typedef vec_detail::by_name_impl<Underlying, Dim> base;
        friend struct vec_detail::by_name_impl<Underlying, Dim>;
        typedef typename base::by_name_type by_name_type;
    
        union{
            by_name_type by_name;
            underlying_type as_array[Dim];
        } member;
    };
    
    #include <iostream>
    
    int main(){
        typedef vec<4, int> vec4i;
        // If this assert triggers, switch to a better compiler
        static_assert(sizeof(vec4i) == sizeof(int) * 4, "Crappy compiler!");
        vec4i f;
        f.w() = 5;
        std::cout << f[3] << '\n';
    }
    
    union Vec3
    {
      struct {double x, y, z;};
      double v[3];
    };
    
    template<size_t D>
    class  Vec
    {
    private: 
      float data[D];
    
    public:  // Constants
      static const size_t num_coords = D;
    
    public:  // Coordinate Accessors
      float& x()             { return data[0]; }
      const float& x() const { return data[0]; }
      float& y()             { static_assert(D>1, "Invalid y()"); return data[1]; }
      const float& y() const { static_assert(D>1, "Invalid y()"); return data[1]; }
      float& z()             { static_assert(D>2, "Invalid z()"); return data[2]; }
      const float& z() const { static_assert(D>2, "Invalid z()"); return data[2]; }
    
    public: // Vector accessors
      float& operator[](size_t index) {return data[index];}
      const float& operator[](size_t index) const {return data[index];}
    
    public:  // Constructor
      Vec() {
        memset(data, 0, sizeof(data));
      }
    
    public:  // Explicit conversion
      template<size_t D2>
      explicit Vec(const Vec<D2> &other) {
        memset(data, 0, sizeof(data));
        memcpy(data, other.data, std::min(D, D2));
      }
    };
    
    template<size_t D>
    struct Detail
    {
      template<size_t C>
      static float sqr_sum(const Vec<D> &v) {
        return v[C]*v[C] + sqr_sum<C-1>(v);
      }
    
      template<>
      static float sqr_sum<0>(const Vec<D> &v) {
        return v[0]*v[0];
      }
    };
    
    template<size_t D>
    float sqr_sum(const Vec<D> &v) {
      return Detail<D>::sqr_sum<D-1>(v);
    }
    
    int main()
    { 
      Vec<3> a;
      a.x() = 2;
      a.y() = 3;
      std::cout << a[0] << " " << a[1] << std::endl;
      std::cout << sqr_sum(a) << std::endl;;
    
      return 0;
    }