C++ 引用嵌套成员的安全方法

C++ 引用嵌套成员的安全方法,c++,pointer-to-member,offsetof,C++,Pointer To Member,Offsetof,我有一个struct,其他一些structs作为成员。外部结构和内部结构都是标准布局(甚至可以假设内部结构是普通的旧数据)。大概是这样的: struct Inner1{ int a=0,b=0; }; 结构Inner2{ int c=0,d=0; }; 结构外部{ 1 x; std::字符串s; 内侧2 y; }; 我想写一些函数,它接受外部&和某种类型的对象T,可以返回任何嵌套字段的值,具体取决于参数: int-get(外部&o,T字段); 若Outer是一个平面结构,那个么指向成员的指针

我有一个
struct
,其他一些
struct
s作为成员。外部结构和内部结构都是
标准布局
(甚至可以假设内部结构是普通的旧数据)。大概是这样的:

struct Inner1{
int a=0,b=0;
};
结构Inner2{
int c=0,d=0;
};
结构外部{
1 x;
std::字符串s;
内侧2 y;
};
我想写一些函数,它接受
外部&
和某种类型的对象
T
,可以返回任何嵌套字段的值,具体取决于参数:

int-get(外部&o,T字段);
Outer
是一个平面结构,那个么指向成员的指针正是我所需要的,但它不是平面的

简单的方法是使
T
成为所有字段的
enum
,并编写一个
开关
,但效率不高。更快的方法是将
T
设为偏移量并写入

int-get(外部&o,大小字段){
return*reinterpret\u cast(reinterpret\u cast(o)+字段);
}
然后像
get(o,offsetof(Outer,y)+offsetof(Inner2,c))
那样调用它。它是有效的,但我不确定它是否保证有效——像这样求和偏移量是否正确,以及按偏移量取成员值是否安全

所以,问题是:这样安全吗?如果没有,有安全的方法吗?构建
T
的值可以是任意复杂的,但是使用它们应该很快

动机:我需要将一些嵌套字段中的值按某种顺序放入,在启动时已知,但在编译时不知道。我想在启动时创建一个
T
数组,然后在获取特定对象时,使用这个预先计算好的数组

[UPD]:所以它将像这样使用:

void条(int);
void foo(外部和外部、向量和字段){
用于(自动字段:字段(&F){
棒(得到(o,场));
}
}
我确实认为这是安全的(因为没有违反严格的别名)

然而,该语言确实有更好的机制来实现这一点:指向数据成员的指针,这些数据成员基本上编译为偏移量

需要注意的是,您必须为
Inner1
Inner2

int get(Outer& o, int Inner1::* m) {
    return o.x.*m;
}

int get(Outer& o, int Inner2::* m) {
    return o.y.*m;
}

int foo() {
  Outer tmp;
  return get(tmp, &Inner1::a) + get(tmp, &Inner2::d);
}

我确实认为这是安全的(因为没有违反严格的别名)

然而,该语言确实有更好的机制来实现这一点:指向数据成员的指针,这些数据成员基本上编译为偏移量

需要注意的是,您必须为
Inner1
Inner2

int get(Outer& o, int Inner1::* m) {
    return o.x.*m;
}

int get(Outer& o, int Inner2::* m) {
    return o.y.*m;
}

int foo() {
  Outer tmp;
  return get(tmp, &Inner1::a) + get(tmp, &Inner2::d);
}


您可以使用函数模板专门化实现相同的功能,请参见下面的示例代码

#include <iostream>

using namespace std;

struct Inner1 {
int a = 1, b = 2;
};
struct Inner2 {
int c = 3, d = 4;
};
struct Outer {
Inner1 x;
std::string s;
Inner2 y;
};

template<typename T>
int get(Outer&o);

template<>
int get<Inner1>(Outer& o)
{
 return o.x.a;
}

template<>
int get<Inner2>(Outer& o)
{
  return o.y.c;
}

int main()
{
  Outer out;
  std::cout << get<Inner1>(out)  << std::endl;
  std::cout << get<Inner2>(out)  << std::endl;

  return 0;
}
#包括
使用名称空间std;
结构Inner1{
INTA=1,b=2;
};
结构Inner2{
int c=3,d=4;
};
结构外部{
1 x;
std::字符串s;
内侧2 y;
};
模板
int get(外部&o);
模板
int get(外部和外部)
{
返回o.x.a;
}
模板
int get(外部和外部)
{
返回o.y.c;
}
int main()
{
外部输出;

std::cout您可以使用函数模板专门化实现相同的功能,请参见下面的示例代码

#include <iostream>

using namespace std;

struct Inner1 {
int a = 1, b = 2;
};
struct Inner2 {
int c = 3, d = 4;
};
struct Outer {
Inner1 x;
std::string s;
Inner2 y;
};

template<typename T>
int get(Outer&o);

template<>
int get<Inner1>(Outer& o)
{
 return o.x.a;
}

template<>
int get<Inner2>(Outer& o)
{
  return o.y.c;
}

int main()
{
  Outer out;
  std::cout << get<Inner1>(out)  << std::endl;
  std::cout << get<Inner2>(out)  << std::endl;

  return 0;
}
#包括
使用名称空间std;
结构Inner1{
INTA=1,b=2;
};
结构Inner2{
int c=3,d=4;
};
结构外部{
1 x;
std::字符串s;
内侧2 y;
};
模板
int get(外部&o);
模板
int get(外部和外部)
{
返回o.x.a;
}
模板
int get(外部和外部)
{
返回o.y.c;
}
int main()
{
外部输出;

你可以这样做

/* main.cpp */

#include <string>
#include <iostream>

using namespace std;

struct Inner1 {
    int a = 0, b = 0;
};

struct Inner2 {
    int c = 0, d = 0;
};

struct Outer {
    Inner1 x;
    std::string s;
    Inner2 y;
};

struct OuterMember
 {
  int (*getter)(Outer &obj);
 };

inline int get(Outer &obj,OuterMember field) { return field.getter(obj); }

template <auto Ptr1,auto Ptr2>
auto GetInnerMember(Outer &obj) { return (obj.*Ptr1).*Ptr2; }

inline constexpr OuterMember OuterMemberA = { GetInnerMember<&Outer::x,&Inner1::a> } ; 

inline constexpr OuterMember OuterMemberB = { GetInnerMember<&Outer::x,&Inner1::b> } ; 

inline constexpr OuterMember OuterMemberC = { GetInnerMember<&Outer::y,&Inner2::c> } ; 

inline constexpr OuterMember OuterMemberD = { GetInnerMember<&Outer::y,&Inner2::d> } ; 

/* main() */

int main()
 {
  Outer obj;

  obj.x.a=1;
  obj.x.b=2;
  obj.y.c=3;
  obj.y.d=4;

  cout << "a = " << get(obj,OuterMemberA) << endl ;
  cout << "b = " << get(obj,OuterMemberB) << endl ;
  cout << "c = " << get(obj,OuterMemberC) << endl ;
  cout << "d = " << get(obj,OuterMemberD) << endl ;

  return 0;
 }
/*main.cpp*/
#包括
#包括
使用名称空间std;
结构Inner1{
int a=0,b=0;
};
结构Inner2{
int c=0,d=0;
};
结构外部{
1 x;
std::字符串s;
内侧2 y;
};
结构外部成员
{
内部(*getter)(外部和obj);
};
内联int get(Outer&obj,OuterMember字段){return field.getter(obj);}
模板
自动获取内部成员(外部&obj){return(obj.*Ptr1)。*Ptr2;}
内联constexpr OuterMember OuterMemberA={GetInnerMember};
inline constexpr OuterMember OuterMemberB={GetInnerMember};
inline constexpr OuterMember OuterMemberC={GetInnerMember};
inline constexpr OuterMember OuterMemberD={GetInnerMember};
/*main()*/
int main()
{
外obj;
obj.x.a=1;
obj.x.b=2;
对象y.c=3;
对象y.d=4;

你可以这样做

/* main.cpp */

#include <string>
#include <iostream>

using namespace std;

struct Inner1 {
    int a = 0, b = 0;
};

struct Inner2 {
    int c = 0, d = 0;
};

struct Outer {
    Inner1 x;
    std::string s;
    Inner2 y;
};

struct OuterMember
 {
  int (*getter)(Outer &obj);
 };

inline int get(Outer &obj,OuterMember field) { return field.getter(obj); }

template <auto Ptr1,auto Ptr2>
auto GetInnerMember(Outer &obj) { return (obj.*Ptr1).*Ptr2; }

inline constexpr OuterMember OuterMemberA = { GetInnerMember<&Outer::x,&Inner1::a> } ; 

inline constexpr OuterMember OuterMemberB = { GetInnerMember<&Outer::x,&Inner1::b> } ; 

inline constexpr OuterMember OuterMemberC = { GetInnerMember<&Outer::y,&Inner2::c> } ; 

inline constexpr OuterMember OuterMemberD = { GetInnerMember<&Outer::y,&Inner2::d> } ; 

/* main() */

int main()
 {
  Outer obj;

  obj.x.a=1;
  obj.x.b=2;
  obj.y.c=3;
  obj.y.d=4;

  cout << "a = " << get(obj,OuterMemberA) << endl ;
  cout << "b = " << get(obj,OuterMemberB) << endl ;
  cout << "c = " << get(obj,OuterMemberC) << endl ;
  cout << "d = " << get(obj,OuterMemberD) << endl ;

  return 0;
 }
/*main.cpp*/
#包括
#包括
使用名称空间std;
结构Inner1{
int a=0,b=0;
};
结构Inner2{
int c=0,d=0;
};
结构外部{
1 x;
std::字符串s;
内侧2 y;
};
结构外部成员
{
内部(*getter)(外部和obj);
};
内联int get(Outer&obj,OuterMember字段){return field.getter(obj);}
模板
自动获取内部成员(外部&obj){return(obj.*Ptr1)。*Ptr2;}
内联constexpr OuterMember OuterMemberA={GetInnerMember};
inline constexpr OuterMember OuterMemberB={GetInnerMember};
inline constexpr OuterMember OuterMemberC={GetInnerMember};
inline constexpr OuterMember OuterMemberD={GetInnerMember};
/*main()*/
int main()
{
外obj;
obj.x.a=1;
obj.x.b=2;
对象y.c=3;
对象y.d=4;

我看不出使用偏移量的好处。它要求调用方知道该偏移量,因此您也可以将指向成员的指针作为参数,以避免混淆
reinterpret\u cast
谢谢,但我认为它们不适用于嵌套成员。使用
offsetof
应该可以。它是唯一受支持的编译器获取被包含成员地址的方法,因为没有关于填充位置和填充内容的标准。但是
offsetof
是一个C函数。为什么您认为
enum
switch
速度慢且有点令人困惑。很难看到这样做不够的情况。可能会有数百个字段和
开关
以线性时间运行,因此速度会很慢。@mihaild我希望开关优化为查找表,这样时间就可以恒定。我看不出使用偏移量的好处。它要求调用者知道偏移量,所以你可以