[英]Accessing struct members with array subscript operator
假设有一个T型和一个只有T型元素统一的结构。
struct Foo {
T one,
T two,
T three
};
我想以休闲方式访问它们:
struct Foo {
T one,
T two,
T three
T &operator [] (int i)
{
return *(T*)((size_t)this + i * cpp_offsetof(Foo, two));
}
};
其中cpp_offsetof
宏(被认为是正确的)是:
#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8)
C ++标准不能保证这一点,但是我们可以假设成员之间的距离是固定的偏移量,并且以上是正确的跨平台解决方案吗?
100%兼容的解决方案是:
struct Foo {
T one,
T two,
T three
T &operator [] (int i) {
const size_t offsets[] = { cpp_offsetof(Foo, one), cpp_offsetof(Foo, two), cpp_offsetof(Foo, three) };
return *(T*)((size_t)this + offsets[i]);
}
};
snk_kid使用指向数据成员的指针 提出了[edit]标准,兼容和更快的版本 [/ edit]
但它需要额外的查找表,我想避免这种情况。
//编辑
还有一个。 我不能仅使用数组和常量来索引这些字段,它们必须被命名为结构的字段(某些宏需要这样做)。
//编辑2
为什么必须将这些命名为结构的字段? 什么是宏? 它是一个较大的项目的设置系统。 简化如下:
struct Foo {
int one;
int two;
}
foo;
struct Setting { void *obj, size_t filed_offset, const char *name, FieldType type }
#define SETTING(CLASS, OBJ, FIELD, TYPE) { OBJ, cpp_offsetof(CLASS, FIELD), #OBJ #FIELD, TYPE }
Setting settings[] = {
SETTING(Foo, foo, one, INT_FIELD),
SETTING(Foo, foo, two, INT_FIELD)
};
再说一次:我不是在寻找100%兼容的解决方案,而是99%的兼容解决方案。 我问我们是否可以期望某些编译器将非均匀填充放在统一字段之间。
您的代码不适用于使用虚拟成员函数的NON-POD类型。 使用指向数据成员的指针,有一种符合标准(高效)的方法来实现您要执行的操作:
template< typename T >
struct Foo {
typedef size_t size_type;
private:
typedef T Foo<T>::* const vec[3];
static const vec v;
public:
T one;
T two;
T three;
const T& operator[](size_type i) const {
return this->*v[i];
}
T& operator[](size_type i) {
return this->*v[i];
}
};
template< typename T >
const typename Foo<T>::vec Foo<T>::v = { &Foo<T>::one, &Foo<T>::two, &Foo<T>::three };
只需确保将const每个与指向数据成员的指针表一起使用即可获得优化。 检查这里查看我在说什么。
如果您要实现的目标仍然是编译时功能,则可以使用模板专门化。
class Foo {
T one;
T two;
T three;
};
template <int i> T & get(Foo& foo);
template T& get<1>(Foo& foo){ return foo.one;}
template T& get<2>(Foo& foo){ return foo.two;}
template T& get<3>(Foo& foo){ return foo.three;}
将get定义为成员函数会很好,但是您不能专门化模板成员函数。 现在,如果这只是您要寻找的编译时扩展,那么这将避免先前文章之一的查找表问题。 如果需要运行时解析,则显然需要一个查找表。
-Brad Phelan http://xtargets.heroku.com
您可能能够使用数组保存数据来实现所需的功能(这样就可以在不使用查找表的情况下获得索引访问)并拥有对各种数组元素的引用(因此您可以使用“命名”元素供您使用)宏)。
我不确定您的宏需要什么,因此我不是100%确信这会起作用,但是可能。 另外,我不确定查找表方法的微小开销是否值得避免太多。 另一方面,我认为我在这里建议的方法没有比指针表方法更复杂的方法,因此在这里供您考虑:
#include <stdio.h>
template< typename T >
struct Foo {
private:
T data_[3];
public:
T& one;
T& two;
T& three;
const T& operator[](size_t i) const {
return data_[i];
}
T& operator[](size_t i) {
return data_[i];
}
Foo() :
one( data_[0]),
two( data_[1]),
three( data_[2])
{};
};
int main()
{
Foo<int> foo;
foo[0] = 11;
foo[1] = 22;
foo[2] = 33;
printf( "%d, %d, %d\n", foo.one, foo.two, foo.three);
Foo<int> const cfoo( foo);
printf( "%d, %d, %d\n", cfoo[0], cfoo[1], cfoo[2]);
return 0;
}
您不能这样做,因为编译器可以在成员之间添加无效字节以允许填充。
有两种方法可以做您想要的。
第一种是使用特定于编译器的关键字或编译指示宏,这将强制编译器不添加填充字节。 但这不是便携式的。 也就是说,这可能是满足您的宏要求的最简单方法,因此我建议您探索这种可能性,并准备在使用不同的编译器时添加更多的实用性。
另一种方法是首先确保您的成员对齐,然后添加访问器:
struct Foo {
T members[ 3 ]; // arrays are guarrantied to be contigu
T& one() { return members[0]; }
const T& one() const { return members[0]; }
//etc...
};
如果您确定所使用的编译器将为此生成正确的代码(我想他们会假设T始终不是引用类型),那么最好的办法就是使用某种检查结构是否按照您的想法进行布局。 我想不出任何特殊原因在相同类型的相邻成员之间插入非均匀填充,但是如果您手动检查结构布局,则至少会知道它是否发生。
例如,如果结构(S)恰好具有N个类型T的成员,则可以在编译时使用sizeof
来检查它们是否紧密包装:
struct S {
T a,b,c;
};
extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
如果编译成功,那么它们将被紧密包装,因为没有其他空间。
如果您刚好有N个成员,并且要确保它们直接一个接一个地放置,则可以使用offsetof
做类似的事情:
class S {
char x;
T a,b,c;
};
extern const char check_b_offset[offsetof(S,b)==offsetof(S,a)+sizeof(T)?1:-1];
extern const char check_c_offset[offsetof(S,c)==offsetof(S,b)+sizeof(T)?1:-1];
根据编译器的不同,这可能必须成为运行时检查,可能不使用offsetof
-无论如何,您可能要对非POD类型执行此操作,因为未为它们定义offsetof
。
S tmp;
assert(&tmp.b==&tmp.a+1);
assert(&tmp.c==&tmp.b+1);
这并没有说明断言开始失败时的处理方法,但是您至少应该得到一些警告,认为这些假设不成立...
(顺便说一句,在char引用中插入适当的强制类型转换,以此类推。为简洁起见,我省略了它们。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.