0

我有一个struct和其他一些structs 作为成员。外部和内部结构都是StandardLayout(甚至可以假设内部是普通的旧数据)。像这样的东西:

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

我想编写一些函数,该函数采用Outer&某种类型的对象T,可以返回任何嵌套字段的值,具体取决于参数:

int get(Outer& o, T field);

如果Outer是一个平面结构,指向成员的指针正是我所需要的,但它不是平面的。

最简单的方法是对所有字段进行Taenum并编写 a switch,但它效率不高。更快的方法是进行T偏移并写入

int get(Outer& o, size_t field) {
    return *reinterpret_cast<int*>(reinterpret_cast<char*>(o) + field);
}

然后像get(o, offsetof(Outer, y) + offsetof(Inner2, c)). 它可以工作,但我不确定它是否可以保证工作 - 对这样的偏移量求和是否正确,以及通过偏移量获取成员值是否安全。

那么问题来了:这种方式安全吗?如果没有,有没有安全的方法?构造的值T可以是任意复杂的,但是使用它们应该很快。

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

[UPD]:所以它会这样使用:

void bar(int);
void foo(Outer& o, vector<T>& fields) {
    for (auto& field : fields) {
        bar(get(o, field));
    }
}
4

3 回答 3

1

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

然而,该语言确实有一个更好的机制来做到这一点:指向数据成员的指针,它编译成基本上是一个偏移量。

需要注意的是,您必须为Inner1Inner2

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);
}

于 2020-05-05T18:52:06.747 回答
1

你可以这样做。

/* 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;
 }
于 2020-05-05T20:10:08.620 回答
0

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

#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;
}

我希望这有帮助!。更有趣的是,这是类型安全的。

于 2020-05-05T18:54:23.493 回答