31

是否可以在 C++ 中遍历 Struct 或 Class 以查找其所有成员?例如,如果我有结构 a 和类 b:

struct a
{
  int a;
  int b;
  int c;
}

class b
{
  public:
    int a;
    int b;
  private:
    int c;
}

是否有可能让他们循环说得到一个打印语句,说“结构 a 有 int 命名为 a、b、c”或“b 类有 int 命名为 a、b、c”

4

6 回答 6

32

有几种方法可以做到这一点,但您需要使用一些宏来定义或调整结构。

您可以使用此答案REFLECTABLE中给出的宏来定义这样的结构:

struct A
{
    REFLECTABLE
    (
        (int) a,
        (int) b,
        (int) c
    )
};

然后您可以遍历字段并像这样打印每个值:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

A x;
print_fields(x);

另一种方法是将结构调整为融合序列(参见文档)。这是一个例子:

struct A
{
    int a;
    int b;
    int c;
};

BOOST_FUSION_ADAPT_STRUCT
(
    A,
    (int, a)
    (int, b)
    (int, c)
)

然后,您也可以使用以下方法打印字段:

struct print_visitor
{
    template<class Index, class C>
    void operator()(Index, C & c)
    {

        std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call() 
                  << "=" 
                  << boost:::fusion::at<Index>(c) 
                  << std::endl;
    }
};


template<class C>
void print_fields(C & c)
{
    typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
    boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), boost::ref(c), _1));
}
于 2013-09-28T14:59:02.867 回答
20

不,这是不可能的,因为 C++ 中没有反射。

于 2013-09-27T20:13:54.350 回答
3

如果您的成员变量属于同一类型,您可以执行我从 GLM 库中窃取的类似操作:

class Point
{
    Point();// you must re-implement the default constructor if you need one

    union
    {
        struct
        {
            double x;
            double y;
            double z;
        };
        std::array<double, 3> components;
    };
};

诚然,从可维护性的角度来看,这不是最优雅的解决方案,手动计算您拥有的变量数量是自找麻烦。然而,它无需额外的库或宏就可以工作,并且适用于您想要这种行为的大多数情况。

联合不支持自动生成的默认构造函数,因此您需要编写一个告诉对象如何初始化联合的构造函数。

for (double component : point.components)
{
    // do something
}
于 2019-07-04T12:01:11.003 回答
3

如果您有相同类型的成员(就像您在第一个特定示例中所做的那样),并且希望 (a) 具有名称,并且 (b) 是可迭代的,那么您可以将数组与枚举结合起来:

enum names { alice, bob, carl };
struct myStruct;
{
  std::array<int, 3> members;
}

然后你可以

myStruct instance;
// iterate through them...
for (auto elem : instance.members)
{
    // work with each element in sequence
} 
// and call them by name, taking away the need to remember which element is the first, etc.
instance.members[bob] = 100;

显然不是一个通用的解决方案,但我发现这在我自己的工作中很有用。

于 2017-03-19T23:00:07.933 回答
1

假设所有类成员都属于同一类型,您可以使用称为结构化绑定的 C++17 功能。假设所有成员都是公开的,这将起作用:

struct SI
{
    int x;
    int y;
    int z;
};

struct SD
{
    double x;
    double y;
    double z;
};

template<typename T>
void print(const T &val)
{
    const auto& [a, b, c] = val;
    for (auto elem : {a, b, c})
    {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

这适用于任何具有完全相同(可复制)类型的 3 个公共元素的结构。如果是非公共成员,该功能必须是朋友或成员。然而,这种方法不能轻易扩展到任意数量的元素。

于 2020-06-11T16:37:18.810 回答
1

这是 QCTDevs 答案的改进版本:

class MyClass
{
    union
    {
        struct Memberstruct
        {
            double test0;
            double test1;
            double test2;
        } m;
        array<double, sizeof( Memberstruct ) / sizeof( double )> memberarray;
    };
    bool test() { &(m.test1) == &(memberarray[1]); }
};

要求仍然是所有相同的数据类型,如果需要,您还需要实现默认构造函数。

改进之处在于您不需要手动维护数组的大小。

一个缺点是与没有此解决方法的类相比,语法发生了变化。

于 2021-02-26T14:52:39.640 回答