我有一个枚举,变量名称为 v1、v2...、vn,每个名称都有一个对应的同名类。我想在编译时(尽管我也不知道在运行时是否可行)找到这些类的 sizeof 的总和,如下例所示。你可以使用模板或预处理器来做到这一点吗?
enum class Enum
{
First,
A = First,
B,
C,
Last
};
class A
{
// ...
};
class B
{
// ...
};
class C
{
// ...
};
constexpr size_t GetTotSumSizeOf()
{
// iterate from Enum::First to Enum::Last, summing sizeof the corresponding class
}
static constexpr size_t TotSum = GetTotSumSizeOf();
c++ 不允许您直接迭代枚举类。因此,您可以通过创建一个辅助结构来实现所需的结果,该结构将每个枚举值映射到其对应的类,然后使用模板递归计算总和。 为每个枚举值创建 EnumToClass 结构的模板特化。并使用 GetSizeOfHelper 模板函数递归计算相应类的大小之和。最后使用 GetToSumSizeof 函数调用递归函数并将其包装。
#include <type_traits>
#include <iostream>
enum class Enum
{
First,
A = First,
B,
C,
Last
};
class A
{
// ...
};
class B
{
// ...
};
class C
{
// ...
};
template<Enum E>
struct EnumToClass;
template<>
struct EnumToClass<Enum::A>
{
using Type = A;
};
template<>
struct EnumToClass<Enum::B>
{
using Type = B;
};
template<>
struct EnumToClass<Enum::C>
{
using Type = C;
};
template<Enum E>
constexpr size_t GetSizeOfHelper()
{
if constexpr (E == Enum::Last)
{
return 0;
}
else
{
using ClassType = typename EnumToClass<E>::Type;
return sizeof(ClassType) + GetSizeOfHelper<static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(E) + 1)>();
}
}
constexpr size_t GetTotSumSizeOf()
{
return GetSizeOfHelper<Enum::First>();
}
static constexpr size_t TotSum = GetTotSumSizeOf();
int main()
{
// Use TotalSum
char buffer[TotSum];
// Print the value of TotSum
std::cout << "Total size of the classes: " << TotSum << std::endl;
// Example usage of buffer
A* a_ptr = reinterpret_cast<A*>(&buffer[0]);
B* b_ptr = reinterpret_cast<B*>(&buffer[sizeof(A)]);
C* c_ptr = reinterpret_cast<C*>(&buffer[sizeof(A) + sizeof(B)]);
}
您可以编写一个 constexpr 例程,通过它的 Enum 标识符返回类的信息,然后在您的 constexpr 中汇总该信息
GetTotSumSizeOf
.
Enum 标识符和类名的关联是手动的。语言中没有任何东西将它们联系在一起或强制它们是正确的。
#include <cstddef>
#include <iostream>
#include <type_traits>
using std::cout;
using std::size_t;
using std::underlying_type_t;
enum class Enum
{
First,
A = First,
B,
C,
Last
};
constexpr
auto operator++(Enum& e) -> Enum& {
using UEnum = underlying_type_t<Enum>;
auto ue = static_cast<UEnum>(e);
e = static_cast<Enum>(ue + 1);
return e;
}
class A
{
// ...
};
class B
{
// ...
};
class C
{
// ...
};
constexpr auto SizeBy(Enum e) -> size_t {
if (e == Enum::A) return sizeof(A);
if (e == Enum::B) return sizeof(B);
if (e == Enum::C) return sizeof(C);
return 0;
}
constexpr size_t GetTotSumSizeOf() {
size_t result{};
for (Enum i = Enum::First; i != Enum::Last; ++i) {
result += SizeBy(i);
}
return result;
}
static constexpr size_t TotSum = GetTotSumSizeOf();
int main() {
cout << TotSum << "\n";
}
根据您的评论假设不需要实际的
enum
,您可以通过类型系统列出您的类并在其之上构建标识符生成:
#include <iostream>
#include <type_traits>
#include <cstddef>
template <class...> struct typelist {};
template <class... Classes>
constexpr auto total_sizeof(typelist<Classes...>) {
return (sizeof(Classes) + ...);
}
template <class Class, class... Classes>
constexpr auto class_id(typelist<Classes...>) {
return []<std::size_t... Idx>(std::index_sequence<Idx...>) {
return ((std::is_same_v<Class, Classes> * Idx) + ...);
}(std::index_sequence_for<Classes...>{});
}
////////////////////////////////////////////////////////////////
constexpr typelist<struct A, struct B, struct C> all_classes;
struct A { char a[1]; };
struct B { char b[2]; };
struct C { char c[3]; };
int main() {
static_assert(class_id<A>(all_classes) == 0);
static_assert(class_id<B>(all_classes) == 1);
static_assert(class_id<C>(all_classes) == 2);
static_assert(total_sizeof(all_classes) == sizeof(A) + sizeof(B) + sizeof(C));
}
如评论中所述,一种选择是使用外部脚本生成枚举类和 total_size 变量,但这不是很容易维护。即使您自动将脚本配置为在每次编译之前运行,它也会增加编译时间,而且 IDE 在开发过程中会因未定义的符号而变得疯狂。所以我觉得最好手动指定。
我想出的一个更简单的构造使用单个 static_assert 来确保程序员记得指定与枚举值相同数量的类。
constexpr size_t GetTotSumSizeOf()
{
constexpr size_t LENS[]
{
sizeof(A),
sizeof(B),
sizeof(C),
};
static_assert(sizeof(LENS) / sizeof(*LENS) == (size_t)Enum::Last);
return std::accumulate(LENS, LENS + (size_t)Enum::Last, size_t(0));
}
"但是,这种方法对于枚举变量名称的意外更改和忘记更新相应的 sizeof() 是不安全的,这就是为什么我接受了 곽지훈 的答案,因为我认为它是最可靠的。
\n "