如何检测class是否有成员变量?
How to detect if a class has member variables?
问题
我想检测 class 是否有成员变量,如果有,则静态断言失败。类似于:
struct b {
int a;
}
static_assert(!has_member_variables<b>, "Class should not contain members"). // Error.
struct c {
virtual void a() {}
void other() {}
}
static_assert(!has_member_variables<c>, "Class should not contain members"). // Fine.
struct d : c {
}
static_assert(!has_member_variables<d>, "Class should not contain members"). // Fine.
struct e : b {
}
static_assert(!has_member_variables<e>, "Class should not contain members"). // Error.
struct f : c {
char z;
}
static_assert(!has_member_variables<f>, "Class should not contain members"). // Error.
有没有办法用 SFINAE 模板实现这个?此 class 可能具有继承甚至多重继承与虚函数(尽管基 classes 中没有成员)。
动机
我有一个非常简单的设置如下:
class iFuncRtn {
virtual Status runFunc(Data &data) = 0;
};
template <TRoutine, TSpecialDataType>
class FuncRoutineDataHelper : public iFuncRtn {
Status runFunc(Data &data) {
static_assert(!has_member_variables<TRoutine>, "Routines shouldnt have data members!");
// Prepare special data for routine
TSpecialDataType sData(data);
runFuncImpl(sData);
}
class SpecificRtn :
public FuncRoutineDataHelper<SpecificRtn, MySpecialData> {
virtual Status runFuncImpl(MySpecialData &sData) {
// Calculate based on input
sData.setValue(someCalculation);
}
};
FunctionalityRoutine
被管理,运行 以每个报价单为基础。它们是定制的,可以执行各种各样的任务,例如联系其他设备等。传入的数据可以由例程操作,并保证在每个 tick 执行时传入,直到功能完成。根据DataHelper
class传入正确类型的数据。我不想阻止未来的人错误地将数据添加到功能例程中,因为这不太可能达到他们的预期。为了强制执行此操作,我希望找到一种使用静态断言的方法。
您可以通过编译器执行空基 class 优化来解决此问题,方法是检查从 T
派生的 class 是否与空 [=] 具有相同的大小28=] 具有虚函数:
template<typename T, typename... BaseClasses>
class IsEmpty
{
// sanity check; see the updated demo below
static_assert(IsDerivedFrom<T, BaseClasses...>::value);
struct NonDerived : BaseClasses... { virtual ~NonDerived() = default; };
struct Derived : T { virtual ~Derived() = default; };
public:
inline static constexpr bool value = (sizeof(NonDerived) == sizeof(Derived));
};
这应该适用于单继承和多继承。但是,当使用多重继承时,有必要列出所有基数 classes,像这样:
static_assert(IsEmpty<Derived, Base1, Base2, Base3>::value);
显然,此解决方案排除了 final
classes.
Here's the original demo.(不适用于多重继承)
您必须以某种方式标记 classes。选择一种您喜欢的方式,属性 或某种类型的带有枚举的整数成员。任何制作 sub-classes 的人都必须遵循您的约定才能使其正常工作。
这里的所有其他答案都是这个的一些变体。
任何使用 sizeof 的答案都不能保证这将在平台、编译器甚至同一平台和编译器上的 classes 之间工作,因为很容易将新成员放入默认值class 成员对齐,其中 sizeof 的大小很容易与子 class.
相同
背景:
正如您的代码和问题中所述,所有这些都只是普通和基本的 C 和 C++ 代码,并且在编译时完全解决。编译器会告诉你一个成员是否存在。编译后,它是一堆高效的、无名的机器代码,本身没有任何提示或帮助。
您为函数或数据成员使用的任何名称都会在编译后有效地消失,正如您所知并在那里看到的那样,并且无法通过名称查找任何成员。每个数据成员仅通过其相对于 class 或结构顶部的数字偏移量而为人所知。
像 .Net、Java 和其他系统是为反射而设计的,它是通过名称记住 class 成员的能力,你可以在运行时找到它们,当你编程是 运行.
C++ 中的模板,除非在诸如 .Net 之类的东西上使用混合模式 C++,它们也会在编译时全部解析,并且名称也会全部消失,因此模板本身不会给您带来任何好处。
像Objective-C这样的语言也被写成在缺少某些类型的特殊成员时不一定会失败,类似于你所问的,但在幕后它使用了大量的支持代码和运行时管理来独立跟踪,其中实际函数本身及其代码仍然未知,并依赖其他代码告诉它们成员是否存在或不会在 null 成员上失败。
在纯 C 或 C++ 中,您只需要制作自己的系统,并准确地动态跟踪什么在做什么。您可以创建枚举、列表或名称字符串的字典。这是通常所做的,你只需要给自己留下提示。 class 不能按照定义为未来子 class 提供隐式可见性的方式编译,如果 RTTI.
不使用某种形式
出于这个原因,将类型成员放在 class 上很常见,这可能是一个简单的枚举。我不会指望尺寸或任何可能依赖于平台的东西。
问题
我想检测 class 是否有成员变量,如果有,则静态断言失败。类似于:
struct b {
int a;
}
static_assert(!has_member_variables<b>, "Class should not contain members"). // Error.
struct c {
virtual void a() {}
void other() {}
}
static_assert(!has_member_variables<c>, "Class should not contain members"). // Fine.
struct d : c {
}
static_assert(!has_member_variables<d>, "Class should not contain members"). // Fine.
struct e : b {
}
static_assert(!has_member_variables<e>, "Class should not contain members"). // Error.
struct f : c {
char z;
}
static_assert(!has_member_variables<f>, "Class should not contain members"). // Error.
有没有办法用 SFINAE 模板实现这个?此 class 可能具有继承甚至多重继承与虚函数(尽管基 classes 中没有成员)。
动机
我有一个非常简单的设置如下:
class iFuncRtn {
virtual Status runFunc(Data &data) = 0;
};
template <TRoutine, TSpecialDataType>
class FuncRoutineDataHelper : public iFuncRtn {
Status runFunc(Data &data) {
static_assert(!has_member_variables<TRoutine>, "Routines shouldnt have data members!");
// Prepare special data for routine
TSpecialDataType sData(data);
runFuncImpl(sData);
}
class SpecificRtn :
public FuncRoutineDataHelper<SpecificRtn, MySpecialData> {
virtual Status runFuncImpl(MySpecialData &sData) {
// Calculate based on input
sData.setValue(someCalculation);
}
};
FunctionalityRoutine
被管理,运行 以每个报价单为基础。它们是定制的,可以执行各种各样的任务,例如联系其他设备等。传入的数据可以由例程操作,并保证在每个 tick 执行时传入,直到功能完成。根据DataHelper
class传入正确类型的数据。我不想阻止未来的人错误地将数据添加到功能例程中,因为这不太可能达到他们的预期。为了强制执行此操作,我希望找到一种使用静态断言的方法。
您可以通过编译器执行空基 class 优化来解决此问题,方法是检查从 T
派生的 class 是否与空 [=] 具有相同的大小28=] 具有虚函数:
template<typename T, typename... BaseClasses>
class IsEmpty
{
// sanity check; see the updated demo below
static_assert(IsDerivedFrom<T, BaseClasses...>::value);
struct NonDerived : BaseClasses... { virtual ~NonDerived() = default; };
struct Derived : T { virtual ~Derived() = default; };
public:
inline static constexpr bool value = (sizeof(NonDerived) == sizeof(Derived));
};
这应该适用于单继承和多继承。但是,当使用多重继承时,有必要列出所有基数 classes,像这样:
static_assert(IsEmpty<Derived, Base1, Base2, Base3>::value);
显然,此解决方案排除了 final
classes.
Here's the original demo.(不适用于多重继承)
您必须以某种方式标记 classes。选择一种您喜欢的方式,属性 或某种类型的带有枚举的整数成员。任何制作 sub-classes 的人都必须遵循您的约定才能使其正常工作。
这里的所有其他答案都是这个的一些变体。
任何使用 sizeof 的答案都不能保证这将在平台、编译器甚至同一平台和编译器上的 classes 之间工作,因为很容易将新成员放入默认值class 成员对齐,其中 sizeof 的大小很容易与子 class.
相同背景:
正如您的代码和问题中所述,所有这些都只是普通和基本的 C 和 C++ 代码,并且在编译时完全解决。编译器会告诉你一个成员是否存在。编译后,它是一堆高效的、无名的机器代码,本身没有任何提示或帮助。
您为函数或数据成员使用的任何名称都会在编译后有效地消失,正如您所知并在那里看到的那样,并且无法通过名称查找任何成员。每个数据成员仅通过其相对于 class 或结构顶部的数字偏移量而为人所知。
像 .Net、Java 和其他系统是为反射而设计的,它是通过名称记住 class 成员的能力,你可以在运行时找到它们,当你编程是 运行.
C++ 中的模板,除非在诸如 .Net 之类的东西上使用混合模式 C++,它们也会在编译时全部解析,并且名称也会全部消失,因此模板本身不会给您带来任何好处。
像Objective-C这样的语言也被写成在缺少某些类型的特殊成员时不一定会失败,类似于你所问的,但在幕后它使用了大量的支持代码和运行时管理来独立跟踪,其中实际函数本身及其代码仍然未知,并依赖其他代码告诉它们成员是否存在或不会在 null 成员上失败。
在纯 C 或 C++ 中,您只需要制作自己的系统,并准确地动态跟踪什么在做什么。您可以创建枚举、列表或名称字符串的字典。这是通常所做的,你只需要给自己留下提示。 class 不能按照定义为未来子 class 提供隐式可见性的方式编译,如果 RTTI.
不使用某种形式出于这个原因,将类型成员放在 class 上很常见,这可能是一个简单的枚举。我不会指望尺寸或任何可能依赖于平台的东西。