通过Struct和Class成员迭代

时间:2013-09-27 20:02:50

标签: c++ class struct

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

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

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

是否可以循环它们以获得一个打印语句说“struct a has int named a,b,c”或“Class b has int named a,b,c”

6 个答案:

答案 0 :(得分:27)

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

您可以使用this answer中给出的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);

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

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

答案 1 :(得分:14)

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

答案 2 :(得分:2)

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

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[bob] = 100;

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

答案 3 :(得分:0)

假设您的成员变量具有相同的类型,则可以执行从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
}

答案 4 :(得分:0)

假定所有类成员都属于同一类型,则可以使用称为结构化绑定的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个公共元素的任何结构一起使用。如果是非公开成员,则该功能必须是朋友或成员。但是,这种方法不能轻易扩展到任意数量的元素。

答案 5 :(得分:0)

这是 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]); }
};

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

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

与没有此解决方法的类相比,缺点是语法有所改变。