是否可以在 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”
有几种方法可以做到这一点,但您需要使用一些宏来定义或调整结构。
您可以使用此答案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));
}
不,这是不可能的,因为 C++ 中没有反射。
如果您的成员变量属于同一类型,您可以执行我从 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
}
如果您有相同类型的成员(就像您在第一个特定示例中所做的那样),并且希望 (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;
显然不是一个通用的解决方案,但我发现这在我自己的工作中很有用。
假设所有类成员都属于同一类型,您可以使用称为结构化绑定的 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 个公共元素的结构。如果是非公共成员,该功能必须是朋友或成员。然而,这种方法不能轻易扩展到任意数量的元素。
这是 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]); }
};
要求仍然是所有相同的数据类型,如果需要,您还需要实现默认构造函数。
改进之处在于您不需要手动维护数组的大小。
一个缺点是与没有此解决方法的类相比,语法发生了变化。