如何保证std::vector<Vec3>与float数组内存布局等价适配OpenGL
问题分析
- 你的猜测是正确的:类内的
GLfloat& x这类引用成员本质是占用指针大小存储空间的常量指针,64位环境下每个引用占8字节,3个引用就会额外增加24字节开销,再加上内存对齐的影响,Vec3的大小远大于3个GLfloat的总大小(12字节),自然无法和裸数组等价。 - 要满足OpenGL的内存布局要求,
Vec3需要是标准布局类型,且大小严格等于3 * sizeof(GLfloat),没有任何额外成员开销。
最优实现方案
推荐使用匿名union+结构体的实现,无任何额外性能开销,同时保留.x/.y/.z直接访问和[]下标访问语法,内存布局和裸GLfloat数组完全一致:
#include <type_traits> #include <GL/gl.h> struct Vec3 { union { // 用于下标访问的底层数组 GLfloat _data[3]; // 用于成员访问的三个分量 struct { GLfloat x, y, z; }; }; // 构造函数 Vec3(GLfloat x, GLfloat y, GLfloat z) : x(x), y(y), z(z) {} // 下标访问重载 GLfloat& operator[](size_t i) { return _data[i]; } const GLfloat& operator[](size_t i) const { return _data[i]; } }; // 编译期校验布局合法性,不符合要求直接编译报错 static_assert(sizeof(Vec3) == 3 * sizeof(GLfloat), "Vec3 大小不符合要求"); static_assert(std::is_standard_layout_v<Vec3>, "Vec3 不是标准布局类型");
方案说明
- C++11及以后标准明确保证,union中公共初始序列的访问是合法行为,不存在未定义风险。
- 该实现下
std::vector<Vec3>的data()指针可以直接强制转为const GLfloat*传入OpenGL相关接口,和vertices2的内存布局100%等价。
替代方案(无union)
如果不希望使用union,也可以用getter/setter函数实现,同样无额外开销,只是访问分量时需要加括号:
struct Vec3 { private: GLfloat _data[3]; public: Vec3(GLfloat x, GLfloat y, GLfloat z) : _data{x, y, z} {} GLfloat& x() { return _data[0]; } GLfloat& y() { return _data[1]; } GLfloat& z() { return _data[2]; } const GLfloat& x() const { return _data[0]; } const GLfloat& y() const { return _data[1]; } const GLfloat& z() const { return _data[2]; } GLfloat& operator[](size_t i) { return _data[i]; } const GLfloat& operator[](size_t i) const { return _data[i]; } }; static_assert(sizeof(Vec3) == 3 * sizeof(GLfloat), "Vec3 大小不符合要求"); static_assert(std::is_standard_layout_v<Vec3>, "Vec3 不是标准布局类型");
内容的提问来源于stack exchange,提问作者mpen
相关产品推荐
相关产品推荐

