Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用数组下标运算符访问结构成员_C++_Cross Platform_Memory Alignment - Fatal编程技术网

C++ 使用数组下标运算符访问结构成员

C++ 使用数组下标运算符访问结构成员,c++,cross-platform,memory-alignment,C++,Cross Platform,Memory Alignment,设一个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)); } }; 其中,宏

设一个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\u偏移量(被认为是正确的)为:

[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%。我想问的是,我们是否可以预期一些编译器会在统一字段之间放置非统一的填充。

你不能,因为编译器可以在成员之间添加死字节以允许填充

有两种方法可以做你想做的事

第一种方法是使用编译器特定的关键字或pragma宏,强制编译器不添加填充字节。但这是不可移植的。 也就是说,这可能是处理宏需求的最简单方法,因此我建议您探索这种可能性,并准备在使用不同的编译器时添加更多pragma

另一种方法是首先确保成员对齐,然后添加访问者:

struct Foo {

   T members[ 3 ]; // arrays are guarrantied to be contigu


   T& one() { return members[0]; } 
   const T& one() const { return members[0]; } 
   //etc... 

};

您的代码不适用于使用虚拟成员函数的非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 };
模板
结构Foo{
typedef size_t size_type;
私人:
typedef T Foo::*常量向量[3];
静态常数v;
公众:
T一;
T二;
t3;
常数T和运算符[](尺寸类型i)常数{
返回此->*v[i];
}
T&operator[](尺寸\类型i){
返回此->*v[i];
}
};
模板
const typename Foo::vec Foo::v={&Foo::one,&Foo::two,&Foo::three};

只需确保在指向数据成员的指针表中使用constevery即可获得优化。查看我在说什么。

如果您试图实现的仍然是编译时功能,另一种方法是使用模板专门化

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;}
class-Foo{
T一;
T二;
t3;
};
模板T&get(Foo&Foo);
模板T&get(Foo&Foo){return Foo.one;}
模板T&get(Foo&Foo){return Foo.two;}
模板T&get(Foo&Foo){return Foo.three;}
最好将get定义为成员函数,但不能 专门化模板成员函数。现在,如果这只是一个编译时 您正在寻找的扩展将避免查找表 以前一个员额的问题。如果需要运行时解析 显然,您需要一个查找表

-- 布拉德·费兰

使用数组保存数据(这样您就可以在不使用查找表的情况下获得索引访问)并引用各种数组元素(这样您就可以有“命名”元素供宏使用),您可能能够实现所需的功能

我不确定你的宏需要什么,所以我不是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;
}
#包括
模板
结构Foo{
私人:
T数据_u3];
公众:
T&one;
T&2;
T&3;
常量和运算符[](大小i)常量{
返回数据_ui];
}
T&T操作员[](尺寸i){
返回数据_ui];
}
Foo():
一(数据[0]),
两个(数据[1]),
三(数据[2])
{};
};
int main()
{
富富,;
foo[0]=11;
foo[1]=22;
foo[2]=33;
printf(“%d,%d,%d\n”,foo.one,foo.two,foo.three);
Foo const cfoo(Foo);
printf(“%d,%d,%d\n”,cfoo[0],cfoo[1],cfoo[2]);
返回0;
}

如果您确定所使用的编译器将为此生成正确的代码(我想他们会这样做,假设T不是引用类型),那么最好的做法是进行某种检查,确保结构按照您的想法进行布局。我想不出在相同类型的相邻成员之间插入非统一填充的具体原因,但是如果手动检查结构布局,那么至少可以知道是否发生了这种情况

例如,如果结构正好有N个类型为T的成员,您可以在编译时使用
sizeof
检查它们是否紧密打包:

struct S {
    T a,b,c;
};

extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
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];
如果这是编译的,那么它们是紧密打包的,因为没有空间容纳任何其他内容

如果恰好有N个成员,您希望确保这些成员一个接一个地直接放置,则可以使用
offsetof
执行类似操作:

struct S {
    T a,b,c;
};

extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
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引用插入适当的强制转换等等。为了简洁起见,我省略了它们。)

首先,我想问的是,我们是否可以假设每个字段的填充(如果添加)都是固定的。其次,我要求提供跨平台解决方案。第三,必须有命名字段,而不是方法。然后,在C++中,不可能在最后一个要求中杀死任何其他可能性。也许你可以添加更多的宏来让你的类与你的宏一起工作,但这真的是一种糟糕的风格和邪恶。编程效率很高