C++ 如何以名称和数组形式访问成员?
我有很多向量类。我有一个2D点<代码> VEC2YT,一个3D点<代码> VEC3YT和4D点<代码> VEC4YT(当你有图形的时候,你经常想要这些,这是图形代码,但是问题有一个通用的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成员 我有很多近似重复的代码用于运算符重载计算,比如距离、叉积、矩阵乘法等等 我曾经有过一些错误,当我没有为子类显式声明操作符时,情况就是这样。复制品让我很烦 此外,我还希望以数组的形式访问这些成员
x
和y
vec3_t
子类vec2_t
,并具有第三个成员z
vec4\u t
子类vec3\u t
并添加w
成员
我有很多近似重复的代码用于运算符重载计算,比如距离、叉积、矩阵乘法等等
我曾经有过一些错误,当我没有为子类显式声明操作符时,情况就是这样。复制品让我很烦
此外,我还希望以数组的形式访问这些成员;这对于某些具有数组参数的OpenGL函数非常有用
我想,也许有了vec\t
模板,我就可以不用子类化就创建向量类了。然而,这带来了两个问题:
vec.x
比vec.d[0]
或其他imo产品好得多,如果可能的话,我想保留它一种方法是:
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;
}